diff options
-rw-r--r-- | auto_tests/toxav_basic_test.c | 30 | ||||
-rw-r--r-- | auto_tests/toxav_many_test.c | 26 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | other/apidsl/toxav.in.h | 128 | ||||
-rw-r--r-- | testing/av_test.c | 74 | ||||
-rw-r--r-- | toxav/Makefile.inc | 61 | ||||
-rw-r--r-- | toxav/audio.c | 282 | ||||
-rw-r--r-- | toxav/audio.h | 65 | ||||
-rw-r--r-- | toxav/bwcontroler.c | 207 | ||||
-rw-r--r-- | toxav/bwcontroler.h | 37 | ||||
-rw-r--r-- | toxav/group.c | 12 | ||||
-rw-r--r-- | toxav/msi.c | 620 | ||||
-rw-r--r-- | toxav/msi.h | 35 | ||||
-rw-r--r-- | toxav/rtp.c | 744 | ||||
-rw-r--r-- | toxav/rtp.h | 139 | ||||
-rw-r--r-- | toxav/toxav.c | 1178 | ||||
-rw-r--r-- | toxav/toxav.h | 226 | ||||
-rw-r--r-- | toxav/toxav_old.c | 4 | ||||
-rw-r--r-- | toxav/video.c | 292 | ||||
-rw-r--r-- | toxav/video.h | 65 | ||||
-rw-r--r-- | toxcore/Messenger.c | 6 | ||||
-rw-r--r-- | toxcore/assoc.c | 8 | ||||
-rw-r--r-- | toxcore/assoc.h | 4 | ||||
-rw-r--r-- | toxcore/logger.c | 18 | ||||
-rw-r--r-- | toxcore/logger.h | 19 | ||||
-rw-r--r-- | toxcore/network.c | 12 | ||||
-rw-r--r-- | toxcore/util.c | 29 | ||||
-rw-r--r-- | toxcore/util.h | 6 |
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 | } |
565 | END_TEST | 573 | END_TEST |
566 | 574 | ||
567 | 575 | #ifndef HAVE_LIBCHECK | |
576 | int main(int argc, char *argv[]) | ||
577 | { | ||
578 | (void) argc; | ||
579 | (void) argv; | ||
580 | |||
581 | test_AV_flows(); | ||
582 | return 0; | ||
583 | } | ||
584 | #else | ||
568 | Suite *tox_suite(void) | 585 | Suite *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) | |||
331 | END_TEST | 338 | END_TEST |
332 | 339 | ||
333 | 340 | ||
334 | 341 | #ifndef HAVE_LIBCHECK | |
335 | 342 | int main(int argc, char *argv[]) | |
343 | { | ||
344 | (void) argc; | ||
345 | (void) argv; | ||
346 | |||
347 | test_AV_three_calls(); | ||
348 | return 0; | ||
349 | } | ||
350 | #else | ||
336 | Suite *tox_suite(void) | 351 | Suite *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" | |||
33 | BUILD_AV="yes" | 33 | BUILD_AV="yes" |
34 | BUILD_TESTING="yes" | 34 | BUILD_TESTING="yes" |
35 | 35 | ||
36 | LOGGING="no" | 36 | TOX_LOGGER="no" |
37 | LOGGING_OUTNAM="libtoxcore.log" | 37 | LOGGING_OUTNAM="libtoxcore.log" |
38 | 38 | ||
39 | NCURSES_FOUND="no" | 39 | NCURSES_FOUND="no" |
@@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir], | |||
82 | ] | 82 | ] |
83 | ) | 83 | ) |
84 | 84 | ||
85 | AC_ARG_ENABLE([log], | 85 | AC_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 | */ |
275 | bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) { | 286 | bool 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 | ******************************************************************************/ |
349 | enum class CALL_CONTROL { | 364 | enum 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 | */ |
394 | bool call_control (uint32_t friend_number, CALL_CONTROL control) { | 409 | bool 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 | ******************************************************************************/ |
415 | error for set_bit_rate { | 434 | namespace 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 | } | ||
429 | namespace 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. |
465 | namespace 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 | } |
185 | void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, | 182 | void 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 | } | ||
193 | void 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 | } |
201 | void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) | 188 | void 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 | |||
400 | int main (int argc, char** argv) | 388 | int 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 @@ | |||
1 | if BUILD_AV | 1 | if BUILD_AV |
2 | 2 | ||
3 | lib_LTLIBRARIES += libtoxav.la | 3 | lib_LTLIBRARIES += libtoxav.la |
4 | libtoxav_la_include_HEADERS = ../toxav/toxav.h | 4 | libtoxav_la_include_HEADERS = ../toxav/toxav.h |
5 | libtoxav_la_includedir = $(includedir)/tox | 5 | libtoxav_la_includedir = $(includedir)/tox |
6 | 6 | ||
7 | libtoxav_la_SOURCES = ../toxav/rtp.h \ | 7 | libtoxav_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 | ||
23 | libtoxav_la_CFLAGS = -I../toxcore \ | 24 | libtoxav_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 | ||
30 | libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ | 31 | libtoxav_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 | ||
36 | libtoxav_la_LIBADD = libtoxcore.la \ | 37 | libtoxav_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 | ||
42 | endif \ No newline at end of file | 43 | endif \ No newline at end of file |
diff --git a/toxav/audio.c b/toxav/audio.c index ff1e1782..3ba95c03 100644 --- a/toxav/audio.c +++ b/toxav/audio.c | |||
@@ -19,6 +19,10 @@ | |||
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 @@ | |||
29 | static struct JitterBuffer *jbuf_new(uint32_t capacity); | 33 | static struct JitterBuffer *jbuf_new(uint32_t capacity); |
30 | static void jbuf_clear(struct JitterBuffer *q); | 34 | static void jbuf_clear(struct JitterBuffer *q); |
31 | static void jbuf_free(struct JitterBuffer *q); | 35 | static void jbuf_free(struct JitterBuffer *q); |
32 | static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); | 36 | static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m); |
33 | static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); | 37 | static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); |
34 | OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); | 38 | OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); |
35 | bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, | 39 | bool 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); |
37 | bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); | 41 | bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels); |
38 | 42 | ||
39 | 43 | ||
40 | 44 | ||
41 | ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data) | 45 | ACSession *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 | ||
106 | DECODER_CLEANUP: | 101 | DECODER_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 | } |
114 | void ac_kill(ACSession* ac) | 109 | void 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 | } |
129 | void ac_do(ACSession* ac) | 123 | void 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 | } |
193 | int ac_queue_message(void* acp, struct RTPMessage_s *msg) | 191 | int 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 | } |
224 | int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) | 222 | int 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 | } |
233 | int 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 | ||
245 | struct JitterBuffer { | 237 | struct 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 | ||
253 | static struct JitterBuffer *jbuf_new(uint32_t capacity) | 245 | static 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 | } |
291 | static int jbuf_write(struct JitterBuffer *q, RTPMessage *m) | 283 | static 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 | } |
317 | static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) | 309 | static 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 | } |
343 | OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) | 335 | OpusEncoder *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 | ||
389 | FAILURE: | 381 | FAILURE: |
390 | opus_encoder_destroy(rc); | 382 | opus_encoder_destroy(rc); |
391 | return NULL; | 383 | return NULL; |
392 | } | 384 | } |
393 | bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, | 385 | bool 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 | } |
421 | bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels) | 414 | bool 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 | ||
32 | struct RTPMessage_s; | 32 | struct RTPMessage; |
33 | 33 | ||
34 | /* | ||
35 | * Base Audio Codec session type. | ||
36 | */ | ||
37 | typedef struct ACSession_s { | 34 | typedef 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 | /* | 58 | ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data); |
68 | * Create new Audio Codec session. | 59 | void ac_kill(ACSession *ac); |
69 | */ | 60 | void ac_iterate(ACSession *ac); |
70 | ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data); | 61 | int ac_queue_message(void *acp, struct RTPMessage *msg); |
71 | /* | 62 | int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); |
72 | * Kill the Audio Codec session. | ||
73 | */ | ||
74 | void ac_kill(ACSession* ac); | ||
75 | /* | ||
76 | * Do periodic work. Work is consisted out of decoding only. | ||
77 | */ | ||
78 | void ac_do(ACSession* ac); | ||
79 | /* | ||
80 | * Queue new rtp message. | ||
81 | */ | ||
82 | int ac_queue_message(void *acp, struct RTPMessage_s *msg); | ||
83 | /* | ||
84 | * Set new values to the encoders. | ||
85 | */ | ||
86 | int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); | ||
87 | int 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 | |||
40 | struct 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 | |||
62 | int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object); | ||
63 | void send_update(BWControler *bwc); | ||
64 | |||
65 | BWControler *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 | } | ||
87 | void 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 | } | ||
97 | void 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 | } | ||
106 | void 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 | } | ||
130 | void 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 | |||
140 | struct 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 */ | ||
148 | typedef char __fail_if_misaligned [ sizeof(struct BWCMessage) == 9 ? 1 : -1 ]; | ||
149 | |||
150 | void 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 | } | ||
176 | int 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 | } | ||
200 | int 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 | |||
26 | typedef struct BWControler_s BWControler; | ||
27 | |||
28 | BWControler *bwc_new(Messenger *m, uint32_t friendnumber, | ||
29 | void (*mcb) (BWControler *, uint32_t, float, void *), | ||
30 | void *udata); | ||
31 | void bwc_kill(BWControler *bwc); | ||
32 | |||
33 | void bwc_feed_avg(BWControler *bwc, uint32_t bytes); | ||
34 | void bwc_add_lost(BWControler *bwc, uint32_t bytes); | ||
35 | void 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 | ||
54 | typedef enum { | 53 | typedef 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 | ||
67 | GENERIC_HEADER ( Request, MSIRequest ); | 67 | GENERIC_HEADER (Request, MSIRequest); |
68 | GENERIC_HEADER ( Error, MSIError ); | 68 | GENERIC_HEADER (Error, MSIError); |
69 | GENERIC_HEADER ( Capabilities, uint8_t ); | 69 | GENERIC_HEADER (Capabilities, uint8_t); |
70 | GENERIC_HEADER ( VFPSZ, uint16_t ); | ||
71 | 70 | ||
72 | 71 | ||
73 | typedef struct { | 72 | typedef 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 | ||
81 | void msg_init (MSIMessage *dest, MSIRequest request); | 79 | void msg_init (MSIMessage *dest, MSIRequest request); |
82 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); | 80 | int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length); |
83 | uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); | 81 | uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length); |
84 | static int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ); | 82 | static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg); |
85 | int send_error ( Messenger* m, uint32_t friend_number, MSIError error ); | 83 | int send_error (Messenger *m, uint32_t friend_number, MSIError error); |
86 | static int invoke_callback(MSICall* call, MSICallbackID cb); | 84 | static int invoke_callback(MSICall *call, MSICallbackID cb); |
87 | static MSICall *get_call ( MSISession *session, uint32_t friend_number ); | 85 | static MSICall *get_call (MSISession *session, uint32_t friend_number); |
88 | MSICall *new_call ( MSISession *session, uint32_t friend_number ); | 86 | MSICall *new_call (MSISession *session, uint32_t friend_number); |
89 | void kill_call ( MSICall *call ); | 87 | void kill_call (MSICall *call); |
90 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); | 88 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); |
91 | void handle_push ( MSICall *call, const MSIMessage *msg ); | 89 | void handle_init (MSICall *call, const MSIMessage *msg); |
92 | void handle_pop ( MSICall *call, const MSIMessage *msg ); | 90 | void handle_push (MSICall *call, const MSIMessage *msg); |
93 | void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object ); | 91 | void handle_pop (MSICall *call, const MSIMessage *msg); |
92 | void 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 | */ |
99 | void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id) | 98 | void 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 | } |
105 | MSISession *msi_new ( Messenger *m ) | 107 | MSISession *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 | } |
135 | int msi_kill ( MSISession *session ) | 137 | int 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 | } |
163 | int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities ) | 170 | int 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 | } |
200 | int msi_hangup ( MSICall* call ) | 211 | int 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 | } |
222 | int msi_answer ( MSICall* call, uint8_t capabilities ) | 240 | int 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 | } |
255 | int msi_change_capabilities( MSICall* call, uint8_t capabilities ) | 277 | int 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 | */ |
293 | void msg_init(MSIMessage* dest, MSIRequest request) | 315 | void 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 | } |
299 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) | 321 | int 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 | } |
384 | uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ) | 395 | uint8_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 | } |
402 | int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ) | 413 | int 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 | } |
453 | int send_error ( Messenger* m, uint32_t friend_number, MSIError error ) | 458 | int 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 | } |
469 | int invoke_callback(MSICall* call, MSICallbackID cb) | 474 | int 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 | ||
483 | FAILURE: | 489 | FAILURE: |
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 | } |
492 | static MSICall *get_call ( MSISession *session, uint32_t friend_number ) | 499 | static 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 | } |
501 | MSICall *new_call ( MSISession *session, uint32_t friend_number ) | 508 | MSICall *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 | } |
552 | void kill_call ( MSICall *call ) | 560 | void 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 | ||
581 | CLEAR_CONTAINER: | 589 | CLEAR_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 | } |
587 | void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data) | 595 | void 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 | } |
614 | void handle_push ( MSICall *call, const MSIMessage *msg ) | 622 | void 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; | ||
678 | FAILURE: | ||
679 | send_error(call->session->messenger, call->friend_number, call->error); | ||
680 | kill_call(call); | ||
681 | } | ||
682 | void 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 | ||
713 | FAILURE: | 731 | FAILURE: |
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 | } |
717 | void handle_pop ( MSICall *call, const MSIMessage *msg ) | 735 | void 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 | } |
757 | void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object ) | 779 | void 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 | */ |
107 | typedef int msi_action_cb ( void *av, MSICall* call); | 104 | typedef 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 | */ |
128 | MSISession *msi_new ( Messenger *m ); | 125 | MSISession *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 | */ |
132 | int msi_kill ( MSISession *session ); | 129 | int msi_kill(MSISession *session); |
133 | /** | 130 | /** |
134 | * Callback setter. | 131 | * Callback setter. |
135 | */ | 132 | */ |
136 | void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id); | 133 | void 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 | */ |
140 | int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities ); | 137 | int 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 | */ |
144 | int msi_hangup ( MSICall* call ); | 141 | int msi_hangup(MSICall *call); |
145 | /** | 142 | /** |
146 | * Answer call request. | 143 | * Answer call request. |
147 | */ | 144 | */ |
148 | int msi_answer ( MSICall* call, uint8_t capabilities ); | 145 | int msi_answer(MSICall *call, uint8_t capabilities); |
149 | /** | 146 | /** |
150 | * Change capabilities of the call. | 147 | * Change capabilities of the call. |
151 | */ | 148 | */ |
152 | int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); | 149 | int 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) | 36 | int 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 | 39 | RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber, | |
52 | typedef 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 | |||
60 | typedef 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 | |||
72 | RTPHeader *parse_header_in ( const uint8_t *payload, int length ); | ||
73 | RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); | ||
74 | uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload ); | ||
75 | uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload ); | ||
76 | int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); | ||
77 | int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); | ||
78 | void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber ); | ||
79 | |||
80 | |||
81 | RTPSession *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 | } |
137 | void rtp_kill ( RTPSession *session ) | 74 | void 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 | } | ||
159 | int 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 | } |
208 | int rtp_start_receiving(RTPSession* session) | 84 | int 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 | } |
228 | int rtp_stop_receiving(RTPSession* session) | 98 | int 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 | } |
239 | int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy ) | 108 | int 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 | } | ||
294 | void 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 | ||
307 | RTPHeader *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 | } |
366 | RTPExtHeader *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; | 188 | bool chloss (const RTPSession *session, const struct RTPHeader *header) |
406 | } | ||
407 | uint8_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, ×tamp, 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 | } |
443 | uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload ) | 208 | struct 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 | } |
468 | int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) | 226 | int 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)); |
561 | int 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 */ | ||
363 | NEW_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 | } |
591 | void 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 | */ |
33 | enum { | 32 | enum { |
34 | rtp_TypeAudio = 192, | 33 | rtp_TypeAudio = 192, |
35 | rtp_TypeVideo, | 34 | rtp_TypeVideo, |
36 | }; | 35 | }; |
37 | 36 | ||
38 | enum { | 37 | struct 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 */ |
47 | typedef 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 | */ | ||
59 | typedef 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. | 68 | typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ]; |
67 | */ | 69 | |
68 | typedef struct RTPMessage_s { | 70 | struct 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[]; | 78 | typedef 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 | */ |
79 | typedef struct { | 83 | typedef 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 | */ | ||
113 | RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ); | ||
114 | /** | ||
115 | * Terminate the session. | ||
116 | */ | ||
117 | void rtp_kill ( RTPSession* session ); | ||
118 | /** | ||
119 | * Do periodical rtp work. | ||
120 | */ | ||
121 | int rtp_do(RTPSession *session); | ||
122 | /** | ||
123 | * By default rtp is in receiving state | ||
124 | */ | ||
125 | int rtp_start_receiving (RTPSession *session); | ||
126 | /** | ||
127 | * Pause rtp receiving mode. | ||
128 | */ | ||
129 | int rtp_stop_receiving (RTPSession *session); | ||
130 | /** | ||
131 | * Sends msg to RTPSession::dest | ||
132 | */ | ||
133 | int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy ); | ||
134 | /** | ||
135 | * Dealloc msg. | ||
136 | */ | ||
137 | void rtp_free_msg ( RTPMessage *msg ); | ||
138 | 100 | ||
101 | RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num, | ||
102 | BWControler *bwc, void *cs, | ||
103 | int (*mcb) (void *, struct RTPMessage *)); | ||
104 | void rtp_kill (RTPSession *session); | ||
105 | int rtp_allow_receiving (RTPSession *session); | ||
106 | int rtp_stop_receiving (RTPSession *session); | ||
107 | int 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 | |||
40 | typedef 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 | ||
48 | typedef struct ToxAVCall_s { | 39 | typedef 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 | ||
82 | struct ToxAV { | 66 | struct 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 | ||
90 | void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data); | ||
107 | 91 | ||
108 | int callback_invite(void* toxav_inst, MSICall* call); | 92 | int callback_invite(void *toxav_inst, MSICall *call); |
109 | int callback_start(void* toxav_inst, MSICall* call); | 93 | int callback_start(void *toxav_inst, MSICall *call); |
110 | int callback_end(void* toxav_inst, MSICall* call); | 94 | int callback_end(void *toxav_inst, MSICall *call); |
111 | int callback_error(void* toxav_inst, MSICall* call); | 95 | int callback_error(void *toxav_inst, MSICall *call); |
112 | int callback_capabilites(void* toxav_inst, MSICall* call); | 96 | int callback_capabilites(void *toxav_inst, MSICall *call); |
113 | 97 | ||
114 | bool audio_bit_rate_invalid(uint32_t bit_rate); | 98 | bool audio_bit_rate_invalid(uint32_t bit_rate); |
115 | bool video_bit_rate_invalid(uint32_t bit_rate); | 99 | bool video_bit_rate_invalid(uint32_t bit_rate); |
116 | bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state); | 100 | bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state); |
117 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); | 101 | ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error); |
118 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); | 102 | ToxAVCall *call_get(ToxAV *av, uint32_t friend_number); |
119 | ToxAVCall* call_remove(ToxAVCall* call); | 103 | ToxAVCall *call_remove(ToxAVCall *call); |
120 | bool call_prepare_transmission(ToxAVCall* call); | 104 | bool call_prepare_transmission(ToxAVCall *call); |
121 | void call_kill_transmission(ToxAVCall* call); | 105 | void call_kill_transmission(ToxAVCall *call); |
122 | void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate); | ||
123 | bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba); | ||
124 | 106 | ||
125 | uint32_t toxav_version_major(void) | 107 | uint32_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 | } |
145 | ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | 127 | ToxAV *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 | ||
193 | END: | 175 | END: |
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 | } |
204 | void toxav_kill(ToxAV* av) | 187 | void 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 | } |
225 | Tox* toxav_get_tox(const ToxAV* av) | 215 | Tox *toxav_get_tox(const ToxAV *av) |
226 | { | 216 | { |
227 | return (Tox*) av->m; | 217 | return (Tox *) av->m; |
228 | } | 218 | } |
229 | uint32_t toxav_iteration_interval(const ToxAV* av) | 219 | uint32_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 | } |
234 | void toxav_iterate(ToxAV* av) | 224 | void 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 | } |
334 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | 276 | bool 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 | |||
312 | END: | ||
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 | } |
372 | void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) | 320 | void 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 | } |
379 | bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) | 327 | bool 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 | |||
419 | END: | 369 | END: |
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 | } |
427 | void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) | 377 | void 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 | } |
434 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) | 384 | bool 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 | ||
570 | END: | 519 | END: |
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 | } |
578 | void toxav_callback_audio_bit_rate_status(ToxAV* av, toxav_audio_bit_rate_status_cb* function, void* user_data) | 527 | bool 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 | } | ||
585 | bool toxav_audio_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error) | ||
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 | |||
653 | END: | 626 | END: |
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 | } |
659 | void toxav_callback_video_bit_rate_status(ToxAV* av, toxav_video_bit_rate_status_cb* function, void* user_data) | 632 | void 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 | } |
666 | bool toxav_video_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error) | 639 | bool 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 | |||
734 | END: | ||
735 | if (error) | ||
736 | *error = rc; | ||
737 | |||
738 | return rc == TOXAV_ERR_SET_BIT_RATE_OK; | ||
739 | } | ||
740 | bool toxav_audio_send_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) | ||
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 | ||
843 | END: | 712 | END: |
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 | } |
849 | bool toxav_video_send_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) | 718 | bool 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 | ||
1015 | END: | 806 | END: |
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 | } |
1021 | void toxav_callback_audio_receive_frame(ToxAV* av, toxav_audio_receive_frame_cb* function, void* user_data) | 812 | void 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 | } |
1028 | void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb* function, void* user_data) | 819 | void 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 | ******************************************************************************/ |
1042 | int callback_invite(void* toxav_inst, MSICall* call) | 833 | void 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 | } | ||
868 | int 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 | } |
1069 | int callback_start(void* toxav_inst, MSICall* call) | 896 | int 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 | } |
1097 | int callback_end(void* toxav_inst, MSICall* call) | 924 | int 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 | } |
1112 | int callback_error(void* toxav_inst, MSICall* call) | 939 | int 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 | } |
1127 | int callback_capabilites(void* toxav_inst, MSICall* call) | 954 | int 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 | } |
1160 | bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state) | 987 | bool 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 | } |
1168 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) | 996 | ToxAVCall *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 | ||
1242 | END: | 1071 | END: |
1072 | |||
1243 | if (error) | 1073 | if (error) |
1244 | *error = rc; | 1074 | *error = rc; |
1245 | 1075 | ||
1246 | return call; | 1076 | return call; |
1247 | } | 1077 | } |
1248 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) | 1078 | ToxAVCall *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 | } |
1256 | ToxAVCall* call_remove(ToxAVCall* call) | 1086 | ToxAVCall *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 | ||
1288 | CLEAR: | 1118 | CLEAR: |
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 | } |
1295 | bool call_prepare_transmission(ToxAVCall* call) | 1125 | bool 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 | ||
1354 | FAILURE: | 1191 | FAILURE: |
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 | } |
1370 | void call_kill_transmission(ToxAVCall* call) | 1208 | void 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 | } |
1398 | void 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 | } | ||
1406 | bool 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 50a1c36b..befae5dc 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 |
83 | typedef struct ToxAV ToxAV; | 90 | typedef 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 | */ |
132 | uint32_t toxav_version_major(void); | 147 | uint32_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 | */ |
136 | uint32_t toxav_version_minor(void); | 152 | uint32_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 | */ |
140 | uint32_t toxav_version_patch(void); | 157 | uint32_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 | */ |
145 | bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); | 163 | bool 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 | */ |
173 | ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); | 194 | ToxAV *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 | */ |
181 | void toxav_kill(ToxAV *toxAV); | 203 | void 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 | */ |
185 | Tox *toxav_get_tox(const ToxAV *toxAV); | 208 | Tox *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 | */ |
195 | uint32_t toxav_iteration_interval(const ToxAV *toxAV); | 220 | uint32_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 | */ |
201 | void toxav_iterate(ToxAV *toxAV); | 227 | void 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 | */ |
249 | bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); | 282 | bool 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 | */ |
257 | typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); | 292 | typedef 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 | */ |
262 | void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data); | 299 | void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data); |
300 | |||
263 | typedef enum TOXAV_ERR_ANSWER { | 301 | typedef 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 | */ |
301 | bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); | 344 | bool 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 | */ |
349 | typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data); | 393 | typedef 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 | */ |
354 | void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data); | 399 | void 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 | |||
396 | typedef enum TOXAV_ERR_CALL_CONTROL { | 443 | typedef 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 | */ |
425 | bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); | 477 | bool 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 | ******************************************************************************/ |
431 | typedef enum TOXAV_ERR_SET_BIT_RATE { | 485 | typedef 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 | */ | ||
462 | typedef 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 | */ | ||
467 | void 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 | */ |
483 | bool toxav_audio_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); | 528 | bool 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 | */ |
497 | typedef void toxav_video_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); | 541 | typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data); |
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 | */ |
502 | void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_status_cb *callback, void *user_data); | 547 | void 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 | */ | ||
518 | bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); | ||
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 | */ |
577 | bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error); | 609 | bool 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 | */ |
593 | bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error); | 628 | bool 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 | */ |
611 | typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void *user_data); | 650 | typedef 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 | */ |
616 | void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data); | 658 | void 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 | */ |
638 | typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); | 681 | typedef 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 | */ |
643 | void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data); | 689 | void 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 | */ | ||
658 | int 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 | */ | ||
671 | int 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 | */ | ||
687 | int 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 | ||
40 | typedef struct { uint16_t size; uint8_t data[]; } Payload; | ||
41 | 39 | ||
42 | bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate); | 40 | bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate); |
43 | 41 | ||
44 | 42 | VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data) | |
45 | VCSession* 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 | ||
94 | BASE_CLEANUP: | 81 | BASE_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 | } |
102 | void vc_kill(VCSession* vc) | 87 | void 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 | } |
119 | void vc_do(VCSession* vc) | 107 | void 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 | } | ||
155 | void 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 | } | ||
163 | int 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 | } | ||
173 | const 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 | } |
199 | int vc_queue_message(void* vcp, struct RTPMessage_s *msg) | 145 | int 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 | |||
288 | end: | ||
289 | rtp_free_msg(msg); | ||
290 | return 0; | 177 | return 0; |
291 | } | 178 | } |
292 | int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height) | 179 | int 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 | ||
315 | bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate) | 204 | bool 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 | ||
39 | struct RTPMessage_s; | 39 | struct RTPMessage; |
40 | 40 | ||
41 | /* | ||
42 | * Base Video Codec session type. | ||
43 | */ | ||
44 | typedef struct VCSession_s { | 41 | typedef 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 | /* | 61 | VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data); |
80 | * Create new Video Codec session. | 62 | void vc_kill(VCSession *vc); |
81 | */ | 63 | void vc_iterate(VCSession *vc); |
82 | VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data, uint32_t mvfpsz); | 64 | int vc_queue_message(void *vcp, struct RTPMessage *msg); |
83 | /* | 65 | int 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 | */ | ||
86 | void vc_kill(VCSession* vc); | ||
87 | /* | ||
88 | * Do periodic work. Work is consisted out of decoding only. | ||
89 | */ | ||
90 | void vc_do(VCSession* vc); | ||
91 | /* | ||
92 | * Set new video splitting cycle. This is requirement in order to send video packets. | ||
93 | */ | ||
94 | void vc_init_video_splitter_cycle(VCSession* vc); | ||
95 | /* | ||
96 | * Update the video splitter cycle with new data. | ||
97 | */ | ||
98 | int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length); | ||
99 | /* | ||
100 | * Iterate over splitted cycle. | ||
101 | */ | ||
102 | const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size); | ||
103 | /* | ||
104 | * Queue new rtp message. | ||
105 | */ | ||
106 | int vc_queue_message(void *vcp, struct RTPMessage_s *msg); | ||
107 | /* | ||
108 | * Set new values to the encoders. | ||
109 | */ | ||
110 | int 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 |
2244 | static time_t lastdump = 0; | 2244 | static time_t lastdump = 0; |
2245 | static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1]; | 2245 | static 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 |
882 | static char *idpart2str(uint8_t *id, size_t len); | 882 | static char *idpart2str(uint8_t *id, size_t len); |
883 | #endif /* LOGGING */ | 883 | #endif /* TOX_LOGGER */ |
884 | 884 | ||
885 | /* refresh buckets */ | 885 | /* refresh buckets */ |
886 | void do_Assoc(Assoc *assoc, DHT *dht) | 886 | void 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 | ||
979 | static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; | 979 | static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; |
980 | static char *idpart2str(uint8_t *id, size_t len) | 980 | static 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 */ |
98 | void kill_Assoc(Assoc *assoc); | 98 | void kill_Assoc(Assoc *assoc); |
99 | 99 | ||
100 | #ifdef LOGGING | 100 | #ifdef TOX_LOGGER |
101 | void Assoc_status(const Assoc *assoc); | 101 | void 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 | ||
47 | struct logger { | 47 | struct 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 | */ |
88 | Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) | 88 | Logger *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 | ||
148 | void logger_kill(Logger *log) | 148 | void 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 | ||
178 | void logger_set_global(Logger *log) | 178 | void 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 | ||
187 | Logger *logger_get_global(void) | 187 | Logger *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 | ||
196 | void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...) | 196 | void 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 | ||
46 | typedef struct logger Logger; | 46 | typedef 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 | } |
237 | void rb_clear(RingBuffer *b) | ||
238 | { | ||
239 | while (!rb_empty(b)) { | ||
240 | void *p; | ||
241 | rb_read(b, &p); | ||
242 | free(p); | ||
243 | } | ||
244 | } | ||
245 | RingBuffer *rb_new(int size) | 237 | RingBuffer *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 | } |
260 | void rb_free(RingBuffer *b) | 252 | void 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 | } |
259 | uint16_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 | } | ||
269 | uint16_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); | |||
64 | bool rb_empty(const RingBuffer *b); | 64 | bool rb_empty(const RingBuffer *b); |
65 | void* rb_write(RingBuffer* b, void* p); | 65 | void* rb_write(RingBuffer* b, void* p); |
66 | bool rb_read(RingBuffer* b, void** p); | 66 | bool rb_read(RingBuffer* b, void** p); |
67 | void rb_clear(RingBuffer *b); | ||
68 | RingBuffer *rb_new(int size); | 67 | RingBuffer *rb_new(int size); |
69 | void rb_free(RingBuffer *b); | 68 | void rb_kill(RingBuffer *b); |
69 | uint16_t rb_size(const RingBuffer *b); | ||
70 | uint16_t rb_data(const RingBuffer* b, void** dest); | ||
71 | |||
70 | #endif /* __UTIL_H__ */ | 72 | #endif /* __UTIL_H__ */ |