New Adaptive BR algorithm, cleanups and fixes

pull/1375/head
Eniz Vukovic 2015-10-10 23:54:23 +02:00
parent bf5e9b89d2
commit d6fdf16520
28 changed files with 2030 additions and 2361 deletions

View File

@ -2,11 +2,20 @@
#include "config.h"
#endif
#ifndef HAVE_LIBCHECK
# include <assert.h>
# define ck_assert(X) assert(X);
# define START_TEST(NAME) void NAME ()
# define END_TEST
#else
# include "helpers.h"
#endif
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <check.h>
#include <stdlib.h>
#include <time.h>
@ -18,7 +27,6 @@
#include "../toxcore/crypto_core.h"
#include "../toxav/toxav.h"
#include "helpers.h"
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define c_sleep(x) Sleep(1*x)
@ -462,19 +470,19 @@ START_TEST(test_AV_flows)
printf("Call started as audio only\n");
printf("Turning on video for Alice...\n");
ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 1000, false, NULL));
ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V);
printf("Turning off video for Alice...\n");
ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 0, false, NULL));
ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V));
printf("Turning off audio for Alice...\n");
ck_assert(toxav_audio_bit_rate_set(AliceAV, 0, 0, false, NULL));
ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A));
@ -564,7 +572,16 @@ START_TEST(test_AV_flows)
}
END_TEST
#ifndef HAVE_LIBCHECK
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
test_AV_flows();
return 0;
}
#else
Suite *tox_suite(void)
{
Suite *s = suite_create("ToxAV");
@ -589,3 +606,4 @@ int main(int argc, char *argv[])
return number_failed;
}
#endif

View File

@ -2,18 +2,25 @@
#include "config.h"
#endif
#ifndef HAVE_LIBCHECK
# include <assert.h>
# define ck_assert(X) assert(X);
# define START_TEST(NAME) void NAME ()
# define END_TEST
#else
# include "helpers.h"
#endif
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <check.h>
#include <stdlib.h>
#include <time.h>
#include <vpx/vpx_image.h>
#include "helpers.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "../toxcore/logger.h"
@ -331,8 +338,16 @@ START_TEST(test_AV_three_calls)
END_TEST
#ifndef HAVE_LIBCHECK
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
test_AV_three_calls();
return 0;
}
#else
Suite *tox_suite(void)
{
Suite *s = suite_create("ToxAV");
@ -362,3 +377,4 @@ int main(int argc, char *argv[])
return number_failed;
}
#endif

View File

@ -33,7 +33,7 @@ BUILD_TESTS="yes"
BUILD_AV="yes"
BUILD_TESTING="yes"
LOGGING="no"
TOX_LOGGER="no"
LOGGING_OUTNAM="libtoxcore.log"
NCURSES_FOUND="no"
@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir],
]
)
AC_ARG_ENABLE([log],
[AC_HELP_STRING([--enable-log], [enable logging (default: auto)]) ],
AC_ARG_ENABLE([logger],
[AC_HELP_STRING([--enable-logger], [enable logging (default: auto)]) ],
[
if test "x$enableval" = "xyes"; then
LOGGING="yes"
TOX_LOGGER="yes"
AC_DEFINE([LOGGING], [], [If logging enabled])
AC_DEFINE([TOX_LOGGER], [], [If logging enabled])
AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value])
AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger])
fi
@ -99,7 +99,7 @@ AC_ARG_WITH(log-level,
AC_HELP_STRING([--with-log-level=LEVEL],
[Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]),
[
if test "x$LOGGING" = "xno"; then
if test "x$TOX_LOGGER" = "xno"; then
AC_MSG_WARN([Logging disabled!])
else
if test "x$withval" = "xTRACE"; then
@ -127,7 +127,7 @@ AC_ARG_WITH(log-path,
AC_HELP_STRING([--with-log-path=DIR],
[Path of logger output]),
[
if test "x$LOGGING" = "xno"; then
if test "x$TOX_LOGGER" = "xno"; then
AC_MSG_WARN([Logging disabled!])
else
AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger])

View File

@ -56,12 +56,19 @@ extern "C" {
/** \subsection threading Threading implications
*
* Unlike the Core API, this API is fully thread-safe. The library will ensure
* the proper synchronisation of parallel calls.
* the proper synchronization of parallel calls.
*
* A common way to run ToxAV (multiple or single instance) is to have a thread,
* separate from tox instance thread, running a simple ${toxAV.iterate} loop,
* sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration.
*
* An important thing to note is that events are triggered from both tox and
* toxav thread (see above). audio and video receive frame events are triggered
* from toxav thread while all the other events are triggered from tox thread.
*
* Tox thread has priority with mutex mechanisms. Any api function can
* fail if mutexes are held by tox thread in which case they will set SYNC
* error code.
*/
/**
@ -231,6 +238,10 @@ bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_ra
* required for the call.
*/
MALLOC,
/**
* Synchronization error occurred.
*/
SYNC,
/**
* The friend number did not designate a valid friend.
*/
@ -273,6 +284,10 @@ event call {
* video sending.
*/
bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
/**
* Synchronization error occurred.
*/
SYNC,
/**
* Failed to initialize codecs for call session. Note that codec initiation
* will fail if there is no receive callback registered for either audio or
@ -347,7 +362,7 @@ event call_state {
*
******************************************************************************/
enum class CALL_CONTROL {
/**
/**
* Resume a previously paused call. Only valid if the pause was caused by this
* client, if not, this control is ignored. Not valid before the call is accepted.
*/
@ -392,6 +407,10 @@ enum class CALL_CONTROL {
* @return true on success.
*/
bool call_control (uint32_t friend_number, CALL_CONTROL control) {
/**
* Synchronization error occurred.
*/
SYNC,
/**
* The friend_number passed did not designate a valid friend.
*/
@ -412,38 +431,7 @@ bool call_control (uint32_t friend_number, CALL_CONTROL control) {
* :: Controlling bit rates
*
******************************************************************************/
error for set_bit_rate {
/**
* The bit rate passed was not one of the supported values.
*/
INVALID,
/**
* The friend_number passed did not designate a valid friend.
*/
FRIEND_NOT_FOUND,
/**
* This client is currently not in a call with the friend.
*/
FRIEND_NOT_IN_CALL,
}
namespace audio {
namespace bit_rate {
event status {
/**
* The function type for the ${event status} callback.
*
* @param friend_number The friend number of the friend for which to set the
* audio bit rate.
* @param stable Is the stream stable enough to keep the bit rate.
* Upon successful, non forceful, bit rate change, this is set to
* true and 'bit_rate' is set to new bit rate.
* The stable is set to false with bit_rate set to the unstable
* bit rate when either current stream is unstable with said bit rate
* or the non forceful change failed.
* @param bit_rate The bit rate in Kb/sec.
*/
typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
}
namespace bit_rate {
/**
* Set the audio bit rate to be used in subsequent audio frames. If the passed
* bit rate is the same as the current bit rate this function will return true
@ -452,51 +440,43 @@ namespace audio {
* forcefully set and the previous non forceful request is cancelled. The active
* non forceful setup will be canceled in favour of new non forceful setup.
*
* @param friend_number The friend number of the friend for which to set the
* audio bit rate.
* @param friend_number The friend number.
* @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
* audio sending.
* @param force True if the bit rate change is forceful.
*
*/
bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate;
}
}
namespace video {
namespace bit_rate {
event status {
/**
* The function type for the ${event status} callback.
*
* @param friend_number The friend number of the friend for which to set the
* video bit rate.
* @param stable Is the stream stable enough to keep the bit rate.
* Upon successful, non forceful, bit rate change, this is set to
* true and 'bit_rate' is set to new bit rate.
* The stable is set to false with bit_rate set to the unstable
* bit rate when either current stream is unstable with said bit rate
* or the non forceful change failed.
* @param bit_rate The bit rate in Kb/sec.
*/
typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
}
/**
* Set the video bit rate to be used in subsequent video frames. If the passed
* bit rate is the same as the current bit rate this function will return true
* without calling a callback. If there is an active non forceful setup with the
* passed video bit rate and the new set request is forceful, the bit rate is
* forcefully set and the previous non forceful request is cancelled. The active
* non forceful setup will be canceled in favour of new non forceful setup.
*
* @param friend_number The friend number of the friend for which to set the
* video bit rate.
* audio sending. Set to -1 to leave unchanged.
* @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
* video sending.
* @param force True if the bit rate change is forceful.
* video sending. Set to -1 to leave unchanged.
*
*/
bool set(uint32_t friend_number, uint32_t video_bit_rate, bool force) with error for set_bit_rate;
}
bool set(uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate) {
/**
* Synchronization error occurred.
*/
SYNC,
/**
* The bit rate passed was not one of the supported values.
*/
INVALID,
/**
* The friend_number passed did not designate a valid friend.
*/
FRIEND_NOT_FOUND,
/**
* This client is currently not in a call with the friend.
*/
FRIEND_NOT_IN_CALL,
}
event status {
/**
* The function type for the ${event status} callback. The event is triggered
* when the network becomes too saturated for current bit rates at which
* point core suggests new bit rates.
*
* @param friend_number The friend number.
* @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
* @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
*/
typedef void(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate);
}
}
/*******************************************************************************
*

View File

@ -28,9 +28,6 @@
#include "../toxcore/util.h"
#include "../toxcore/network.h" /* current_time_monotonic() */
#define LOGGING
#include "../toxcore/logger.h"
/* Playing audio data */
#include <portaudio.h>
/* Reading audio */
@ -53,21 +50,21 @@
#define c_sleep(x) usleep(1000*x)
#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)
#define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X)
// RGB -> YUV
#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
#define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
#define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
#define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
// YUV -> RGB
#define C(Y) ( (Y) - 16 )
#define D(U) ( (U) - 128 )
#define E(V) ( (V) - 128 )
#define C(Y) ((Y) - 16 )
#define D(U) ((U) - 128 )
#define E(V) ((V) - 128 )
#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8)
#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8)
#define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8)
#define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
#define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8)
#define TEST_TRANSFER_A 0
@ -182,21 +179,11 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
free(rb_write(cc->arb, f));
pthread_mutex_unlock(cc->arb_mutex);
}
void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
bool stable, uint32_t bit_rate, void *user_data)
void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
uint32_t audio_bit_rate, uint32_t video_bit_rate,
void *user_data)
{
if (stable)
printf ("Set new audio bit rate to: %d\n", bit_rate);
else
printf ("The network is overly saturated with audio bit rate at: %d\n", bit_rate);
}
void t_toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
bool stable, uint32_t bit_rate, void *user_data)
{
if (stable)
printf ("Set new video bit rate to: %d", bit_rate);
else
printf ("The network is overly saturated with video bit rate at: %d", bit_rate);
printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate);
}
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
@ -216,6 +203,7 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
tox_options_default(&opts);
opts.end_port = 0;
opts.ipv6_enabled = false;
{
TOX_ERR_NEW error;
@ -279,18 +267,16 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
/* Alice */
toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC);
toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
toxav_callback_video_bit_rate_status(*AliceAV, t_toxav_video_bit_rate_status_cb, AliceCC);
toxav_callback_audio_bit_rate_status(*AliceAV, t_toxav_audio_bit_rate_status_cb, AliceCC);
/* Bob */
toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC);
toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
toxav_callback_video_bit_rate_status(*BobAV, t_toxav_video_bit_rate_status_cb, BobCC);
toxav_callback_audio_bit_rate_status(*BobAV, t_toxav_audio_bit_rate_status_cb, BobCC);
printf("Created 2 instances of ToxAV\n");
@ -320,6 +306,9 @@ void* iterate_toxav (void * data)
fflush(stdout);
#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
if (!rc)
rc = 1;
cvWaitKey(rc);
#else
c_sleep(rc);
@ -340,8 +329,8 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
int32_t strides[3] = { 1280, 640, 640 };
uint8_t* planes[3] = {
malloc(img->height * img->width),
malloc(img->height * img->width / 2),
malloc(img->height * img->width / 2),
malloc(img->height * img->width / 4),
malloc(img->height * img->width / 4),
};
int x_chroma_shift = 1;
@ -363,9 +352,9 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
}
}
}
int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL);
int rc = toxav_video_send_frame(av, friend_number, img->width, img->height,
planes[0], planes[1], planes[2], NULL);
free(planes[0]);
free(planes[1]);
free(planes[2]);
@ -396,9 +385,8 @@ int print_help (const char* name)
return 0;
}
int main (int argc, char** argv)
{
{
freopen("/dev/zero", "w", stderr);
Pa_Initialize();
@ -585,7 +573,7 @@ int main (int argc, char** argv)
err = Pa_StartStream(adout);
assert(err == paNoError);
toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
/* Start write thread */
pthread_t t;
@ -593,7 +581,7 @@ int main (int argc, char** argv)
pthread_detach(t);
printf("Sample rate %d\n", af_info.samplerate);
while ( start_time + expected_time > time(NULL) ) {
while (start_time + expected_time > time(NULL) ) {
uint64_t enc_start_time = current_time_monotonic();
int64_t count = sf_read_short(af_handle, PCM, frame_size);
if (count > 0) {
@ -674,7 +662,7 @@ int main (int argc, char** argv)
iterate_tox(bootstrap, AliceAV, BobAV);
/* Start decode thread */
struct toxav_thread_data data = {
struct toxav_thread_data data = {
.AliceAV = AliceAV,
.BobAV = BobAV,
.sig = 0
@ -694,13 +682,13 @@ int main (int argc, char** argv)
time_t start_time = time(NULL);
while(start_time + 90 > time(NULL)) {
IplImage* frame = cvQueryFrame( capture );
IplImage* frame = cvQueryFrame(capture );
if (!frame)
break;
send_opencv_img(AliceAV, 0, frame);
iterate_tox(bootstrap, AliceAV, BobAV);
c_sleep(video_frame_duration);
c_sleep(10);
}
cvReleaseCapture(&capture);

View File

@ -1,42 +1,43 @@
if BUILD_AV
lib_LTLIBRARIES += libtoxav.la
libtoxav_la_include_HEADERS = ../toxav/toxav.h
libtoxav_la_includedir = $(includedir)/tox
lib_LTLIBRARIES += libtoxav.la
libtoxav_la_include_HEADERS = ../toxav/toxav.h
libtoxav_la_includedir = $(includedir)/tox
libtoxav_la_SOURCES = ../toxav/rtp.h \
../toxav/rtp.c \
../toxav/msi.h \
../toxav/msi.c \
../toxav/group.h \
../toxav/group.c \
../toxav/audio.h \
../toxav/audio.c \
../toxav/video.h \
../toxav/video.c \
../toxav/toxav.h \
../toxav/toxav.c \
../toxav/toxav_old.h \
../toxav/toxav_old.c
../toxav/rtp.c \
../toxav/msi.h \
../toxav/msi.c \
../toxav/group.h \
../toxav/group.c \
../toxav/audio.h \
../toxav/audio.c \
../toxav/video.h \
../toxav/video.c \
../toxav/bwcontroler.h \
../toxav/bwcontroler.c \
../toxav/toxav.h \
../toxav/toxav.c \
../toxav/toxav_old.h \
../toxav/toxav_old.c
libtoxav_la_CFLAGS = -I../toxcore \
-I../toxav \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(AV_CFLAGS) \
$(PTHREAD_CFLAGS)
-I../toxav \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(AV_CFLAGS) \
$(PTHREAD_CFLAGS)
libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(WINSOCK2_LIBS)
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(WINSOCK2_LIBS)
libtoxav_la_LIBADD = libtoxcore.la \
$(LIBSODIUM_LIBS) \
$(NACL_LIBS) \
$(PTHREAD_LIBS) \
$(AV_LIBS)
$(LIBSODIUM_LIBS) \
$(NACL_LIBS) \
$(PTHREAD_LIBS) \
$(AV_LIBS)
endif

View File

@ -19,6 +19,10 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#include "audio.h"
@ -29,80 +33,71 @@
static struct JitterBuffer *jbuf_new(uint32_t capacity);
static void jbuf_clear(struct JitterBuffer *q);
static void jbuf_free(struct JitterBuffer *q);
static int jbuf_write(struct JitterBuffer *q, RTPMessage *m);
static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m);
static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels);
bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels);
ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
{
ACSession *ac = calloc(sizeof(ACSession), 1);
if (!ac) {
LOGGER_WARNING("Allocation failed! Application might misbehave!");
return NULL;
}
if (create_recursive_mutex(ac->queue_mutex) != 0) {
LOGGER_WARNING("Failed to create recursive mutex!");
free(ac);
return NULL;
}
int status;
ac->decoder = opus_decoder_create(48000, 2, &status );
if ( status != OPUS_OK ) {
ac->decoder = opus_decoder_create(48000, 2, &status);
if (status != OPUS_OK) {
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
goto BASE_CLEANUP;
}
if ( !(ac->j_buf = jbuf_new(3)) ) {
if (!(ac->j_buf = jbuf_new(3))) {
LOGGER_WARNING("Jitter buffer creaton failed!");
opus_decoder_destroy(ac->decoder);
goto BASE_CLEANUP;
}
/* Initialize encoders with default values */
ac->encoder = create_audio_encoder(48000, 48000, 2);
if (ac->encoder == NULL)
goto DECODER_CLEANUP;
ac->test_encoder = create_audio_encoder(48000, 48000, 2);
if (ac->test_encoder == NULL) {
opus_encoder_destroy(ac->encoder);
goto DECODER_CLEANUP;
}
ac->last_encoding_bit_rate = 48000;
ac->last_encoding_sampling_rate = 48000;
ac->last_encoding_channel_count = 2;
ac->last_test_encoding_bit_rate = 48000;
ac->last_test_encoding_sampling_rate = 48000;
ac->last_test_encoding_channel_count = 2;
ac->last_decoding_channel_count = 2;
ac->last_decoding_sampling_rate = 48000;
ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */
ac->le_bit_rate = 48000;
ac->le_sample_rate = 48000;
ac->le_channel_count = 2;
ac->ld_channel_count = 2;
ac->ld_sample_rate = 48000;
ac->ldrts = 0; /* Make it possible to reconfigure straight away */
/* These need to be set in order to properly
* do error correction with opus */
ac->last_packet_frame_duration = 120;
ac->last_packet_sampling_rate = 48000;
ac->last_packet_channel_count = 1;
ac->lp_frame_duration = 120;
ac->lp_sampling_rate = 48000;
ac->lp_channel_count = 1;
ac->av = av;
ac->friend_number = friend_number;
ac->acb.first = cb;
ac->acb.second = cb_data;
return ac;
DECODER_CLEANUP:
opus_decoder_destroy(ac->decoder);
jbuf_free(ac->j_buf);
@ -111,39 +106,41 @@ BASE_CLEANUP:
free(ac);
return NULL;
}
void ac_kill(ACSession* ac)
void ac_kill(ACSession *ac)
{
if (!ac)
return;
opus_encoder_destroy(ac->encoder);
opus_encoder_destroy(ac->test_encoder);
opus_decoder_destroy(ac->decoder);
jbuf_free(ac->j_buf);
pthread_mutex_destroy(ac->queue_mutex);
LOGGER_DEBUG("Terminated audio handler: %p", ac);
free(ac);
}
void ac_do(ACSession* ac)
void ac_iterate(ACSession *ac)
{
if (!ac)
return;
/* TODO fix this and jitter buffering */
/* Enough space for the maximum frame size (120 ms 48 KHz audio) */
/* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */
int16_t tmp[5760 * 2];
RTPMessage *msg;
struct RTPMessage *msg;
int rc = 0;
pthread_mutex_lock(ac->queue_mutex);
while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
pthread_mutex_unlock(ac->queue_mutex);
if (rc == 2) {
LOGGER_DEBUG("OPUS correction");
int fs = (ac->last_packet_sampling_rate * ac->last_packet_frame_duration) / 1000;
int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000;
rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
} else {
/* Get values from packet and decode. */
@ -156,98 +153,93 @@ void ac_do(ACSession* ac)
rtp_free_msg(msg);
continue;
}*/
/* Pick up sampling rate from packet */
memcpy(&ac->last_packet_sampling_rate, msg->data, 4);
ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate);
ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4);
memcpy(&ac->lp_sampling_rate, msg->data, 4);
ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate);
ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
/** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
* it didn't work quite well.
*/
if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) {
if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
LOGGER_WARNING("Failed to reconfigure decoder!");
rtp_free_msg(msg);
free(msg);
continue;
}
rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
rtp_free_msg(msg);
rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0);
free(msg);
}
if (rc < 0) {
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
} else if (ac->acb.first) {
ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate;
ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->last_packet_channel_count,
ac->last_packet_sampling_rate, ac->acb.second);
ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate;
ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count,
ac->lp_sampling_rate, ac->acb.second);
}
return;
}
pthread_mutex_unlock(ac->queue_mutex);
}
int ac_queue_message(void* acp, struct RTPMessage_s *msg)
int ac_queue_message(void *acp, struct RTPMessage *msg)
{
if (!acp || !msg)
return -1;
if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
LOGGER_WARNING("Got dummy!");
rtp_free_msg(msg);
free(msg);
return 0;
}
if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) {
if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) {
LOGGER_WARNING("Invalid payload type!");
rtp_free_msg(msg);
free(msg);
return -1;
}
ACSession* ac = acp;
ACSession *ac = acp;
pthread_mutex_lock(ac->queue_mutex);
int rc = jbuf_write(ac->j_buf, msg);
pthread_mutex_unlock(ac->queue_mutex);
if (rc == -1) {
LOGGER_WARNING("Could not queue the message!");
rtp_free_msg(msg);
free(msg);
return -1;
}
return 0;
}
int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
{
if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels,
&ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count))
if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate,
sampling_rate, channels,
&ac->le_bit_rate,
&ac->le_sample_rate,
&ac->le_channel_count))
return -1;
LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
return 0;
}
int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
{
if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bit_rate, sampling_rate, channels,
&ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count))
return -1;
LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
return 0;
}
struct JitterBuffer {
RTPMessage **queue;
uint32_t size;
uint32_t capacity;
uint16_t bottom;
uint16_t top;
struct RTPMessage **queue;
uint32_t size;
uint32_t capacity;
uint16_t bottom;
uint16_t top;
};
static struct JitterBuffer *jbuf_new(uint32_t capacity)
@ -260,9 +252,9 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity)
struct JitterBuffer *q;
if ( !(q = calloc(sizeof(struct JitterBuffer), 1)) ) return NULL;
if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL;
if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) {
free(q);
return NULL;
}
@ -275,7 +267,7 @@ static void jbuf_clear(struct JitterBuffer *q)
{
for (; q->bottom != q->top; ++q->bottom) {
if (q->queue[q->bottom % q->size]) {
rtp_free_msg(q->queue[q->bottom % q->size]);
free(q->queue[q->bottom % q->size]);
q->queue[q->bottom % q->size] = NULL;
}
}
@ -288,15 +280,15 @@ static void jbuf_free(struct JitterBuffer *q)
free(q->queue);
free(q);
}
static int jbuf_write(struct JitterBuffer *q, RTPMessage *m)
static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m)
{
uint16_t sequnum = m->header->sequnum;
uint16_t sequnum = m->header.sequnum;
unsigned int num = sequnum % q->size;
if ((uint32_t)(sequnum - q->bottom) > q->size) {
LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
jbuf_clear(q);
q->bottom = sequnum - q->capacity;
q->queue[num] = m;
@ -314,7 +306,7 @@ static int jbuf_write(struct JitterBuffer *q, RTPMessage *m)
return 0;
}
static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
{
if (q->top == q->bottom) {
*success = 0;
@ -324,7 +316,7 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
unsigned int num = q->bottom % q->size;
if (q->queue[num]) {
RTPMessage *ret = q->queue[num];
struct RTPMessage *ret = q->queue[num];
q->queue[num] = NULL;
++q->bottom;
*success = 1;
@ -340,73 +332,74 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
*success = 0;
return NULL;
}
OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
{
int status = OPUS_OK;
OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
if ( status != OPUS_OK ) {
OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
if (status != OPUS_OK) {
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
return NULL;
}
status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
if ( status != OPUS_OK ) {
if (status != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
goto FAILURE;
}
/* Enable in-band forward error correction in codec */
status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1));
if ( status != OPUS_OK ) {
if (status != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
goto FAILURE;
}
/* Make codec resistant to up to 10% packet loss
* NOTE This could also be adjusted on the fly, rather than hard-coded,
* with feedback from the receiving client.
*/
status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10));
if ( status != OPUS_OK ) {
if (status != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
goto FAILURE;
}
/* Set algorithm to the highest complexity, maximizing compression */
status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
if ( status != OPUS_OK ) {
if (status != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
goto FAILURE;
}
return rc;
FAILURE:
opus_encoder_destroy(rc);
return NULL;
}
bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
int32_t* old_br, int32_t* old_sr, int32_t* old_ch)
bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
int32_t *old_br, int32_t *old_sr, int32_t *old_ch)
{
/* Values are checked in toxav.c */
if (*old_sr != new_sr || *old_ch != new_ch) {
OpusEncoder* new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
if (new_encoder == NULL)
return false;
opus_encoder_destroy(*e);
*e = new_encoder;
} else if (*old_br == new_br)
return true; /* Nothing changed */
else {
int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
if ( status != OPUS_OK ) {
if (status != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
return false;
}
@ -415,31 +408,32 @@ bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr,
*old_br = new_br;
*old_sr = new_sr;
*old_ch = new_ch;
return true;
}
bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels)
bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels)
{
if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) {
if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500)
if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) {
if (current_time_monotonic() - ac->ldrts < 500)
return false;
int status;
OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status );
if ( status != OPUS_OK ) {
OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status);
if (status != OPUS_OK) {
LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
return false;
}
ac->last_decoding_sampling_rate = sampling_rate;
ac->last_decoding_channel_count = channels;
ac->last_decoder_reconfiguration = current_time_monotonic();
ac->ld_sample_rate = sampling_rate;
ac->ld_channel_count = channels;
ac->ldrts = current_time_monotonic();
opus_decoder_destroy(ac->decoder);
ac->decoder = new_dec;
LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
}
return true;
}
}

View File

@ -29,61 +29,36 @@
#include "../toxcore/util.h"
struct RTPMessage_s;
struct RTPMessage;
/*
* Base Audio Codec session type.
*/
typedef struct ACSession_s {
/* encoding */
OpusEncoder *encoder;
int32_t last_encoding_sampling_rate;
int32_t last_encoding_channel_count;
int32_t last_encoding_bit_rate;
/* Testing encoder for dynamic bit rate streaming */
OpusEncoder *test_encoder;
int32_t last_test_encoding_sampling_rate;
int32_t last_test_encoding_channel_count;
int32_t last_test_encoding_bit_rate;
int32_t le_sample_rate; /* Last encoder sample rate */
int32_t le_channel_count; /* Last encoder channel count */
int32_t le_bit_rate; /* Last encoder bit rate */
/* decoding */
OpusDecoder *decoder;
int32_t last_packet_channel_count;
int32_t last_packet_sampling_rate;
int32_t last_packet_frame_duration;
int32_t last_decoding_sampling_rate;
int32_t last_decoding_channel_count;
uint64_t last_decoder_reconfiguration;
int32_t lp_channel_count; /* Last packet channel count */
int32_t lp_sampling_rate; /* Last packet sample rate */
int32_t lp_frame_duration; /* Last packet frame duration */
int32_t ld_sample_rate; /* Last decoder sample rate */
int32_t ld_channel_count; /* Last decoder channel count */
uint64_t ldrts; /* Last decoder reconfiguration time stamp */
void *j_buf;
pthread_mutex_t queue_mutex[1];
ToxAV* av;
ToxAV *av;
uint32_t friend_number;
PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
} ACSession;
/*
* Create new Audio Codec session.
*/
ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
/*
* Kill the Audio Codec session.
*/
void ac_kill(ACSession* ac);
/*
* Do periodic work. Work is consisted out of decoding only.
*/
void ac_do(ACSession* ac);
/*
* Queue new rtp message.
*/
int ac_queue_message(void *acp, struct RTPMessage_s *msg);
/*
* Set new values to the encoders.
*/
int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
void ac_kill(ACSession *ac);
void ac_iterate(ACSession *ac);
int ac_queue_message(void *acp, struct RTPMessage *msg);
int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
#endif /* AUDIO_H */
#endif /* AUDIO_H */

207
toxav/bwcontroler.c Normal file
View File

@ -0,0 +1,207 @@
/** bwcontroler.c
*
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <assert.h>
#include "bwcontroler.h"
#include "../toxcore/logger.h"
#include "../toxcore/util.h"
#define BWC_PACKET_ID 196
#define BWC_SEND_INTERVAL_MS 1000
#define BWC_REFRESH_INTERVAL_MS 10000
#define BWC_AVG_PKT_COUNT 20
/**
*
*/
struct BWControler_s {
void (*mcb) (BWControler *, uint32_t, float, void *);
void *mcb_data;
Messenger *m;
uint32_t friend_number;
struct {
uint32_t lru; /* Last recv update time stamp */
uint32_t lsu; /* Last sent update time stamp */
uint32_t lfu; /* Last refresh time stamp */
uint32_t lost;
uint32_t recv;
} cycle;
struct {
uint32_t rb_s[BWC_AVG_PKT_COUNT];
RingBuffer *rb;
} rcvpkt; /* To calculate average received packet */
};
int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
void send_update(BWControler *bwc);
BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
void (*mcb) (BWControler *, uint32_t, float, void *),
void *udata)
{
BWControler *retu = calloc(sizeof(struct BWControler_s), 1);
retu->mcb = mcb;
retu->mcb_data = udata;
retu->m = m;
retu->friend_number = friendnumber;
retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic();
retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
/* Fill with zeros */
int i = 0;
for (; i < BWC_AVG_PKT_COUNT; i ++)
rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
return retu;
}
void bwc_kill(BWControler *bwc)
{
if (!bwc)
return;
m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
rb_kill(bwc->rcvpkt.rb);
free(bwc);
}
void bwc_feed_avg(BWControler* bwc, uint32_t bytes)
{
uint32_t *p;
rb_read(bwc->rcvpkt.rb, (void**) &p);
rb_write(bwc->rcvpkt.rb, p);
*p = bytes;
}
void bwc_add_lost(BWControler *bwc, uint32_t bytes)
{
if (!bwc)
return;
if (!bytes) {
uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1;
rb_data(bwc->rcvpkt.rb, (void**) t_avg);
int i = 0;
for (; i < BWC_AVG_PKT_COUNT; i ++) {
bytes += *(t_avg[i]);
if (*(t_avg[i]))
c++;
}
bytes /= c;
}
bwc->cycle.lost += bytes;
send_update(bwc);
}
void bwc_add_recv(BWControler *bwc, uint32_t bytes)
{
if (!bwc || !bytes)
return;
bwc->cycle.recv += bytes;
send_update(bwc);
}
struct BWCMessage {
uint8_t core_type; /* Aligner for payload type which is always 196 */
uint32_t lost;
uint32_t recv;
} __attribute__((packed));
/* Check alignment */
typedef char __fail_if_misaligned [ sizeof(struct BWCMessage) == 9 ? 1 : -1 ];
void send_update(BWControler *bwc)
{
if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
bwc->cycle.lost /= 10;
bwc->cycle.recv /= 10;
bwc->cycle.lfu = current_time_monotonic();
}
else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
if (bwc->cycle.lost)
{
LOGGER_DEBUG ("%p Sent update", bwc);
struct BWCMessage msg;
msg.core_type = BWC_PACKET_ID;
msg.lost = htonl(bwc->cycle.lost);
msg.recv = htonl(bwc->cycle.recv);
if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, (uint8_t *)&msg, sizeof(msg)))
LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(msg), strerror(errno));
}
bwc->cycle.lsu = current_time_monotonic();
}
}
int on_update (BWControler *bwc, struct BWCMessage *msg)
{
LOGGER_DEBUG ("%p Got update from peer", bwc);
/* Peer must respect time boundary */
if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) {
LOGGER_DEBUG("%p Rejecting extra update", bwc);
return -1;
}
bwc->cycle.lru = current_time_monotonic();
msg->recv = ntohl(msg->recv);
msg->lost = ntohl(msg->lost);
LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost);
if (msg->lost && bwc->mcb)
bwc->mcb(bwc, bwc->friend_number,
((float) (msg->lost) / (msg->recv + msg->lost)),
bwc->mcb_data);
return 0;
}
int bwc_handle_data(Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object)
{
if (length != sizeof(struct BWCMessage))
return;
/* NOTE the data is mutable */
return on_update(object, (struct BWCMessage *) data);
}

37
toxav/bwcontroler.h Normal file
View File

@ -0,0 +1,37 @@
/** bwcontroler.h
*
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BWCONROLER_H
#define BWCONROLER_H
#include "../toxcore/Messenger.h"
typedef struct BWControler_s BWControler;
BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
void (*mcb) (BWControler *, uint32_t, float, void *),
void *udata);
void bwc_kill(BWControler *bwc);
void bwc_feed_avg(BWControler *bwc, uint32_t bytes);
void bwc_add_lost(BWControler *bwc, uint32_t bytes);
void bwc_add_recv(BWControler *bwc, uint32_t bytes);
#endif /* BWCONROLER_H */

View File

@ -20,7 +20,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif /* HAVE_CONFIG_H */
#include "group.h"
#include "../toxcore/util.h"
@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity)
Group_JitterBuffer *q;
if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL;
if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL;
if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) {
free(q);
@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av)
group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
OPUS_APPLICATION_AUDIO, &rc);
if ( rc != OPUS_OK ) {
if (rc != OPUS_OK) {
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
group_av->audio_encoder = NULL;
return -1;
@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av)
rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
if ( rc != OPUS_OK ) {
if (rc != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
opus_encoder_destroy(group_av->audio_encoder);
group_av->audio_encoder = NULL;
@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av)
rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
if ( rc != OPUS_OK ) {
if (rc != OPUS_OK) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
opus_encoder_destroy(group_av->audio_encoder);
group_av->audio_encoder = NULL;
@ -306,7 +306,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int g
int rc;
peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
if ( rc != OPUS_OK ) {
if (rc != OPUS_OK) {
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
free(pk);
return -1;

File diff suppressed because it is too large Load Diff

View File

@ -29,9 +29,6 @@
#include "video.h"
#include "../toxcore/Messenger.h"
/** Preconfigured value for video splitting */
#define VIDEOFRAME_PIECE_SIZE 500
/**
* Error codes.
*/
@ -89,13 +86,13 @@ typedef struct MSICall_s {
uint8_t peer_capabilities; /* Peer capabilities */
uint8_t self_capabilities; /* Self capabilities */
uint16_t peer_vfpsz; /* Video frame piece size */
uint32_t friend_number; /* Index of this call in MSISession */
uint32_t friend_number; /* Index of this call in MSISession */
MSIError error; /* Last error */
void* av_call; /* Pointer to av call handler */
struct MSICall_s* next;
struct MSICall_s* prev;
void *av_call; /* Pointer to av call handler */
struct MSICall_s *next;
struct MSICall_s *prev;
} MSICall;
@ -104,7 +101,7 @@ typedef struct MSICall_s {
* returned the call is considered errored and will be handled
* as such which means it will be terminated without any notice.
*/
typedef int msi_action_cb ( void *av, MSICall* call);
typedef int msi_action_cb (void *av, MSICall *call);
/**
* Control session struct. Please do not modify outside msi.c
@ -114,41 +111,41 @@ typedef struct MSISession_s {
MSICall **calls;
uint32_t calls_tail;
uint32_t calls_head;
void *av;
Messenger *messenger;
pthread_mutex_t mutex[1];
msi_action_cb* callbacks[7];
msi_action_cb *callbacks[7];
} MSISession;
/**
* Start the control session.
*/
MSISession *msi_new ( Messenger *m );
MSISession *msi_new(Messenger *m);
/**
* Terminate control session. NOTE: all calls will be freed
*/
int msi_kill ( MSISession *session );
int msi_kill(MSISession *session);
/**
* Callback setter.
*/
void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id);
void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id);
/**
* Send invite request to friend_number.
*/
int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities );
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
/**
* Hangup call. NOTE: 'call' will be freed
*/
int msi_hangup ( MSICall* call );
int msi_hangup(MSICall *call);
/**
* Answer call request.
*/
int msi_answer ( MSICall* call, uint8_t capabilities );
int msi_answer(MSICall *call, uint8_t capabilities);
/**
* Change capabilities of the call.
*/
int msi_change_capabilities ( MSICall* call, uint8_t capabilities );
int msi_change_capabilities(MSICall *call, uint8_t capabilities);
#endif /* MSI_H */

View File

@ -24,6 +24,7 @@
#endif /* HAVE_CONFIG_H */
#include "rtp.h"
#include "bwcontroler.h"
#include "../toxcore/logger.h"
#include "../toxcore/util.h"
#include "../toxcore/Messenger.h"
@ -31,584 +32,361 @@
#include <stdlib.h>
#include <assert.h>
#define RTCP_REPORT_INTERVAL_MS 500
#define RTP_MAX_SIZE 1500
#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
#define ADD_SETTING_MARKER(_h, _v) do { ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
typedef struct {
uint64_t timestamp; /* in ms */
uint32_t received_packets;
uint32_t expected_packets;
/* ... other stuff in the future */
} RTCPReport;
typedef struct RTCPSession_s {
RTPSession *rtp_session;
uint8_t prefix;
uint64_t last_sent_report_ts;
uint32_t last_received_packets;
uint32_t last_expected_packets;
RingBuffer* pl_stats; /* Packet loss stats over time */
} RTCPSession;
RTPHeader *parse_header_in ( const uint8_t *payload, int length );
RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) )
RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
BWControler *bwc, void *cs,
int (*mcb) (void *, struct RTPMessage *))
{
assert(mcb);
assert(cs);
assert(m);
RTPSession *retu = calloc(1, sizeof(RTPSession));
if ( !retu ) {
if (!retu) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL;
}
retu->version = RTP_VERSION; /* It's always 2 */
retu->ssrc = random_int();
retu->payload_type = payload_type % 128;
retu->ssrc = random_int();
retu->payload_type = payload_type;
retu->m = m;
retu->friend_number = friend_num;
if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(retu);
return NULL;
}
retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
retu->friend_number = friendnumber;
/* Also set payload type as prefix */
retu->prefix = payload_type;
retu->bwc = bwc;
retu->cs = cs;
retu->mcb = mcb;
/* Initialize rtcp session */
if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(retu->csrc);
free(retu);
return NULL;
}
retu->rtcp_session->prefix = payload_type + 2;
retu->rtcp_session->pl_stats = rb_new(4);
retu->rtcp_session->rtp_session = retu;
if (-1 == rtp_start_receiving(retu)) {
if (-1 == rtp_allow_receiving(retu)) {
LOGGER_WARNING("Failed to start rtp receiving mode");
free(retu->rtcp_session);
free(retu->csrc);
free(retu);
return NULL;
}
return retu;
}
void rtp_kill ( RTPSession *session )
void rtp_kill (RTPSession *session)
{
if ( !session ) return;
if (!session)
return;
rtp_stop_receiving (session);
free ( session->ext_header );
free ( session->csrc );
void* t;
while (!rb_empty(session->rtcp_session->pl_stats)) {
rb_read(session->rtcp_session->pl_stats, (void**) &t);
free(t);
}
rb_free(session->rtcp_session->pl_stats);
LOGGER_DEBUG("Terminated RTP session: %p", session);
/* And finally free session */
free ( session->rtcp_session );
free ( session );
rtp_stop_receiving (session);
free (session);
}
int rtp_do(RTPSession *session)
{
if (!session || !session->rtcp_session)
return rtp_StateNormal;
if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
send_rtcp_report(session->rtcp_session, session->m, session->friend_number);
}
if (rb_full(session->rtcp_session->pl_stats)) {
RTCPReport* reports[4];
int i = 0;
for (; i < 4; i++)
rb_read(session->rtcp_session->pl_stats, (void**) reports + i);
/* Check for timed out reports (> 6 sec) */
uint64_t now = current_time_monotonic();
for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++);
for (; i < 4; i ++) {
rb_write(session->rtcp_session->pl_stats, reports[i]);
reports[i] = NULL;
}
if (!rb_empty(session->rtcp_session->pl_stats)) {
for (i = 0; reports[i] != NULL; i ++)
free(reports[i]);
return rtp_StateNormal; /* As some reports are timed out, we need more */
}
/* We have 4 on-time reports so we can proceed */
uint32_t quality = 100;
for (i = 0; i < 4; i++) {
uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets;
quality = MIN(quality, current);
free(reports[i]);
}
if (quality <= 90) {
LOGGER_WARNING("Stream quality: BAD (%d)", quality);
return rtp_StateBad;
} else if (quality >= 99) {
LOGGER_DEBUG("Stream quality: GOOD (%d)", quality);
return rtp_StateGood;
} else {
LOGGER_DEBUG("Stream quality: NORMAL (%d)", quality);
}
}
return rtp_StateNormal;
}
int rtp_start_receiving(RTPSession* session)
int rtp_allow_receiving(RTPSession *session)
{
if (session == NULL)
return -1;
if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix,
handle_rtp_packet, session) == -1) {
if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
handle_rtp_packet, session) == -1) {
LOGGER_WARNING("Failed to register rtp receive handler");
return -1;
}
if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix,
handle_rtcp_packet, session->rtcp_session) == -1) {
LOGGER_WARNING("Failed to register rtcp receive handler");
m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
return -1;
}
LOGGER_DEBUG("Started receiving on session: %p", session);
return 0;
}
int rtp_stop_receiving(RTPSession* session)
int rtp_stop_receiving(RTPSession *session)
{
if (session == NULL)
return -1;
m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
LOGGER_DEBUG("Stopped receiving on session: %p", session);
return 0;
}
int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy )
int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
{
if ( !session ) {
if (!session) {
LOGGER_WARNING("No session!");
return -1;
}
uint8_t parsed[RTP_MAX_SIZE];
uint8_t *it;
RTPHeader header[1];
memset(header, 0, sizeof(header));
ADD_FLAG_VERSION ( header, session->version );
ADD_FLAG_PADDING ( header, session->padding );
ADD_FLAG_EXTENSION ( header, session->extension );
ADD_FLAG_CSRCC ( header, session->cc );
ADD_SETTING_MARKER ( header, session->marker );
if (dummy)
ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 );
else
ADD_SETTING_PAYLOAD ( header, session->payload_type );
uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
memset(rdata, 0, sizeof(rdata));
header->sequnum = session->sequnum;
header->timestamp = current_time_monotonic();
header->ssrc = session->ssrc;
rdata[0] = session->payload_type;
int i;
for ( i = 0; i < session->cc; i++ )
header->csrc[i] = session->csrc[i];
struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
header->length = 12 /* Minimum header len */ + ( session->cc * 4 );
uint32_t parsed_len = length + header->length + 1;
assert(parsed_len + (session->ext_header ? session->ext_header->length * 4 : 0) < RTP_MAX_SIZE );
header->ve = 2;
header->pe = 0;
header->xe = 0;
header->cc = 0;
parsed[0] = session->prefix;
it = parse_header_out ( header, parsed + 1 );
if ( session->ext_header ) {
parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * 4 );
it = parse_ext_header_out ( session->ext_header, it );
header->ma = 0;
header->pt = session->payload_type % 128;
header->sequnum = htons(session->sequnum);
header->timestamp = htonl(current_time_monotonic());
header->ssrc = htonl(session->ssrc);
header->cpart = 0;
header->tlen = htons(length);
if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
/**
* The lenght is lesser than the maximum allowed lenght (including header)
* Send the packet in single piece.
*/
memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
} else {
/**
* The lenght is greater than the maximum allowed lenght (including header)
* Send the packet in multiple pieces.
*/
uint16_t sent = 0;
uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
rdata, piece + sizeof(struct RTPHeader) + 1))
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
piece + sizeof(struct RTPHeader) + 1, strerror(errno));
sent += piece;
header->cpart = htons(sent);
}
/* Send remaining */
piece = length - sent;
if (piece) {
memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
piece + sizeof(struct RTPHeader) + 1))
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
piece + sizeof(struct RTPHeader) + 1, strerror(errno));
}
}
memcpy(it, data, length);
if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) {
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
return -1;
}
session->sequnum ++;
return 0;
}
void rtp_free_msg ( RTPMessage *msg )
bool chloss (const RTPSession *session, const struct RTPHeader *header)
{
if ( msg->ext_header ) {
free ( msg->ext_header->table );
free ( msg->ext_header );
if (ntohl(header->timestamp) < session->rtimestamp) {
uint16_t hosq, lost = 0;
hosq = ntohs(header->sequnum);
lost = (hosq > session->rsequnum) ?
(session->rsequnum + 65535) - hosq :
session->rsequnum - hosq;
puts ("Lost packet");
while (lost --)
bwc_add_lost(session->bwc ,0);
return true;
}
free ( msg->header );
free ( msg );
return false;
}
RTPHeader *parse_header_in ( const uint8_t *payload, int length )
struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
{
if ( !payload || !length ) {
LOGGER_WARNING("No payload to extract!");
return NULL;
}
assert(allocate_len >= data_length);
RTPHeader *retu = calloc(1, sizeof (RTPHeader));
struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL;
}
msg->len = data_length - sizeof(struct RTPHeader);
memcpy(&msg->header, data, data_length);
memcpy(&retu->sequnum, payload, sizeof(retu->sequnum));
retu->sequnum = ntohs(retu->sequnum);
msg->header.sequnum = ntohs(msg->header.sequnum);
msg->header.timestamp = ntohl(msg->header.timestamp);
msg->header.ssrc = ntohl(msg->header.ssrc);
const uint8_t *it = payload + 2;
msg->header.cpart = ntohs(msg->header.cpart);
msg->header.tlen = ntohs(msg->header.tlen);
retu->flags = *it;
++it;
if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
/* Deallocate */
LOGGER_WARNING("Invalid version!");
free(retu);
return NULL;
}
uint8_t cc = GET_FLAG_CSRCC ( retu );
int total = 12 /* Minimum header len */ + ( cc * 4 );
if ( length < total ) {
LOGGER_WARNING("Length invalid!");
free(retu);
return NULL;
}
retu->marker_payloadt = *it;
++it;
retu->length = total;
memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
it += 4;
memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
retu->timestamp = ntohl(retu->timestamp);
retu->ssrc = ntohl(retu->ssrc);
uint8_t x;
for ( x = 0; x < cc; x++ ) {
it += 4;
memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
retu->csrc[x] = ntohl(retu->csrc[x]);
}
return retu;
return msg;
}
RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
{
const uint8_t *it = payload;
RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader));
if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL;
}
memcpy(&retu->length, it, sizeof(retu->length));
retu->length = ntohs(retu->length);
it += 2;
if ( length < ( retu->length * sizeof(uint32_t) ) ) {
LOGGER_WARNING("Length invalid!");
free(retu);
return NULL;
}
memcpy(&retu->type, it, sizeof(retu->type));
retu->type = ntohs(retu->type);
it += 2;
if ( !(retu->table = calloc(retu->length, sizeof (uint32_t))) ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(retu);
return NULL;
}
uint16_t x;
for ( x = 0; x < retu->length; x++ ) {
it += 4;
memcpy(retu->table + x, it, sizeof(*retu->table));
retu->table[x] = ntohl(retu->table[x]);
}
return retu;
}
uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload )
{
uint8_t cc = GET_FLAG_CSRCC ( header );
uint8_t *it = payload;
uint16_t sequnum;
uint32_t timestamp;
uint32_t ssrc;
uint32_t csrc;
/* Add sequence number first */
sequnum = htons(header->sequnum);
memcpy(it, &sequnum, sizeof(sequnum));
it += 2;
*it = header->flags;
++it;
*it = header->marker_payloadt;
++it;
timestamp = htonl(header->timestamp);
memcpy(it, &timestamp, sizeof(timestamp));
it += 4;
ssrc = htonl(header->ssrc);
memcpy(it, &ssrc, sizeof(ssrc));
uint8_t x;
for ( x = 0; x < cc; x++ ) {
it += 4;
csrc = htonl(header->csrc[x]);
memcpy(it, &csrc, sizeof(csrc));
}
return it + 4;
}
uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload )
{
uint8_t *it = payload;
uint16_t length;
uint16_t type;
uint32_t entry;
length = htons(header->length);
memcpy(it, &length, sizeof(length));
it += 2;
type = htons(header->type);
memcpy(it, &type, sizeof(type));
it -= 2; /* Return to 0 position */
if ( header->table ) {
uint16_t x;
for ( x = 0; x < header->length; x++ ) {
it += 4;
entry = htonl(header->table[x]);
memcpy(it, &entry, sizeof(entry));
}
}
return it + 4;
}
int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
{
(void) m;
(void) friendnumber;
RTPSession *session = object;
if ( !session || length < 13 || length > RTP_MAX_SIZE ) {
data ++;
length--;
if (!session || length < sizeof (struct RTPHeader)) {
LOGGER_WARNING("No session or invalid length of received buffer!");
return -1;
}
RTPHeader* header = parse_header_in ( data + 1, length );
if ( !header ) {
LOGGER_WARNING("Could not parse message: Header failed to extract!");
const struct RTPHeader *header = (struct RTPHeader *) data;
if (header->pt != session->payload_type % 128) {
LOGGER_WARNING("Invalid payload type with the session");
return -1;
}
RTPExtHeader* ext_header = NULL;
uint16_t from_pos = header->length + 1;
uint16_t msg_length = length - from_pos;
if ( GET_FLAG_EXTENSION ( header ) ) {
ext_header = parse_ext_header_in ( data + from_pos, length );
if (ntohs(header->cpart) >= ntohs(header->tlen)) {
/* Never allow this case to happen */
return -1;
}
if ( ext_header ) {
msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * 4 );
from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * 4 );
} else { /* Error */
LOGGER_WARNING("Could not parse message: Ext Header failed to extract!");
free(header);
return -1;
bwc_feed_avg(session->bwc, length);
if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
/* The message is sent in single part */
/* Only allow messages which have arrived in order;
* drop late messages
*/
if (chloss(session, header)) {
return 0;
} else {
/* Message is not late; pick up the latest parameters */
session->rsequnum = ntohs(header->sequnum);
session->rtimestamp = ntohl(header->timestamp);
}
bwc_add_recv(session->bwc, length);
/* Invoke processing of active multiparted message */
if (session->mp) {
if (session->mcb)
session->mcb (session->cs, session->mp);
else
free(session->mp);
session->mp = NULL;
}
/* The message came in the allowed time;
* process it only if handler for the session is present.
*/
if (!session->mcb)
return 0;
return session->mcb (session->cs, new_message(length, data, length));
} else {
/* The message is sent in multiple parts */
if (session->mp) {
/* There are 2 possible situations in this case:
* 1) being that we got the part of already processing message.
* 2) being that we got the part of a new/old message.
*
* We handle them differently as we only allow a single multiparted
* processing message
*/
if (session->mp->header.sequnum == ntohs(header->sequnum) &&
session->mp->header.timestamp == ntohl(header->timestamp)) {
/* First case */
/* Make sure we have enough allocated memory */
if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
session->mp->header.tlen <= ntohs(header->cpart)) {
/* There happened to be some corruption on the stream;
* continue wihtout this part
*/
return 0;
}
memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
length - sizeof(struct RTPHeader));
session->mp->len += length - sizeof(struct RTPHeader);
bwc_add_recv(session->bwc, length);
if (session->mp->len == session->mp->header.tlen) {
/* Received a full message; now push it for the further
* processing.
*/
if (session->mcb)
session->mcb (session->cs, session->mp);
else
free(session->mp);
session->mp = NULL;
}
} else {
/* Second case */
if (session->mp->header.timestamp > ntohl(header->timestamp))
/* The received message part is from the old message;
* discard it.
*/
return 0;
/* Measure missing parts of the old message */
bwc_add_lost(session->bwc,
(session->mp->header.tlen - session->mp->len) +
/* Must account sizes of rtp headers too */
((session->mp->header.tlen - session->mp->len) /
MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
/* Push the previous message for processing */
if (session->mcb)
session->mcb (session->cs, session->mp);
else
free(session->mp);
session->mp = NULL;
goto NEW_MULTIPARTED;
}
} else {
/* In this case threat the message as if it was received in order
*/
/* This is also a point for new multiparted messages */
NEW_MULTIPARTED:
/* Only allow messages which have arrived in order;
* drop late messages
*/
if (chloss(session, header)) {
return 0;
} else {
/* Message is not late; pick up the latest parameters */
session->rsequnum = ntohs(header->sequnum);
session->rtimestamp = ntohl(header->timestamp);
}
bwc_add_recv(session->bwc, length);
/* Again, only store message if handler is present
*/
if (session->mcb) {
session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
/* Reposition data if necessary */
if (ntohs(header->cpart));
memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
}
}
}
if (msg_length > RTP_MAX_SIZE) {
LOGGER_WARNING("Could not parse message: Invalid length!");
free(header);
free(ext_header);
return -1;
}
/* Check if message came in late */
if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) {
/* Not late */
if (header->sequnum > session->rsequnum)
session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum;
else if (header->sequnum < session->rsequnum)
session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum;
else /* Usual case when transmission starts */
session->rtcp_session->last_expected_packets ++;
session->rsequnum = header->sequnum;
session->rtimestamp = header->timestamp;
}
session->rtcp_session->last_received_packets ++;
/* Check if the message is dummy. We don't keep dummy messages */
if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) {
LOGGER_DEBUG("Received dummy rtp message");
free(header);
free(ext_header);
return 0;
}
/* Otherwise we will store the message if we have an appropriate handler */
if (!session->mcb) {
LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header));
free(header);
free(ext_header);
return 0;
}
RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length);
if ( !msg ) {
LOGGER_WARNING("Could not parse message: Allocation failed!");
free(header);
free(ext_header);
return -1;
}
msg->header = header;
msg->ext_header = ext_header;
msg->length = msg_length;
memcpy ( msg->data, data + from_pos, msg_length );
return session->mcb (session->cs, msg);
}
int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
{
(void) m;
(void) friendnumber;
if (length < 9)
return -1;
RTCPSession* session = object;
RTCPReport* report = malloc(sizeof(RTCPReport));
memcpy(&report->received_packets, data + 1, 4);
memcpy(&report->expected_packets, data + 5, 4);
report->received_packets = ntohl(report->received_packets);
report->expected_packets = ntohl(report->expected_packets);
if (report->expected_packets == 0 || report->received_packets > report->expected_packets) {
LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets);
free(report);
return 0;
}
report->timestamp = current_time_monotonic();
free(rb_write(session->pl_stats, report));
LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
return 0;
}
void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
{
if (session->last_expected_packets == 0)
return;
uint8_t parsed[9];
parsed[0] = session->prefix;
uint32_t received_packets = htonl(session->last_received_packets);
uint32_t expected_packets = htonl(session->last_expected_packets);
memcpy(parsed + 1, &received_packets, 4);
memcpy(parsed + 5, &expected_packets, 4);
if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
else {
LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
session->last_received_packets = 0;
session->last_expected_packets = 0;
session->last_sent_report_ts = current_time_monotonic();
}
}

View File

@ -22,119 +22,88 @@
#ifndef RTP_H
#define RTP_H
#define RTP_VERSION 2
#include "bwcontroler.h"
#include "../toxcore/Messenger.h"
#include "stdbool.h"
/**
* Payload type identifier. Also used as rtp callback prefix. (Not dummies)
* Payload type identifier. Also used as rtp callback prefix.
*/
enum {
rtp_TypeAudio = 192,
rtp_TypeVideo,
};
enum {
rtp_StateBad = -1,
rtp_StateNormal,
rtp_StateGood,
};
struct RTPHeader {
/* Standard RTP header */
#ifndef WORDS_BIGENDIAN
unsigned cc: 4; /* Contributing sources count */
unsigned xe: 1; /* Extra header */
unsigned pe: 1; /* Padding */
unsigned ve: 2; /* Version */
/**
* Standard rtp header.
*/
typedef struct {
uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
uint16_t sequnum; /* Sequence Number */
uint32_t timestamp; /* Timestamp */
uint32_t ssrc; /* SSRC */
uint32_t csrc[16]; /* CSRC's table */
uint32_t length; /* Length of the header in payload string. */
} RTPHeader;
/**
* Standard rtp extension header.
*/
typedef struct {
uint16_t type; /* Extension profile */
uint16_t length; /* Number of extensions */
uint32_t *table; /* Extension's table */
} RTPExtHeader;
unsigned pt: 7; /* Payload type */
unsigned ma: 1; /* Marker */
#else
unsigned ve: 2; /* Version */
unsigned pe: 1; /* Padding */
unsigned xe: 1; /* Extra header */
unsigned cc: 4; /* Contributing sources count */
/**
* Standard rtp message.
*/
typedef struct RTPMessage_s {
RTPHeader *header;
RTPExtHeader *ext_header;
unsigned ma: 1; /* Marker */
unsigned pt: 7; /* Payload type */
#endif
uint32_t length;
uint8_t data[];
} RTPMessage;
uint16_t sequnum;
uint32_t timestamp;
uint32_t ssrc;
uint32_t csrc[16];
/* Non-standard TOX-specific fields */
uint16_t cpart;/* Data offset of the current part */
uint16_t tlen; /* Total message lenght */
} __attribute__ ((packed));
/* Check alignment */
typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ];
struct RTPMessage {
uint16_t len;
struct RTPHeader header;
uint8_t data[];
} __attribute__ ((packed));
/* Check alignment */
typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ];
/**
* RTP control session.
*/
typedef struct {
uint8_t version;
uint8_t padding;
uint8_t extension;
uint8_t cc;
uint8_t marker;
uint8_t payload_type;
uint16_t sequnum; /* Sending sequence number */
uint16_t rsequnum; /* Receiving sequence number */
uint16_t sequnum; /* Sending sequence number */
uint16_t rsequnum; /* Receiving sequence number */
uint32_t rtimestamp;
uint32_t ssrc;
uint32_t *csrc;
/* If some additional data must be sent via message
* apply it here. Only by allocating this member you will be
* automatically placing it within a message.
*/
RTPExtHeader *ext_header;
/* Msg prefix for core to know when recving */
uint8_t prefix;
struct RTPMessage *mp; /* Expected parted message */
Messenger *m;
int friend_number;
struct RTCPSession_s *rtcp_session;
uint32_t friend_number;
BWControler *bwc;
void *cs;
int (*mcb) (void*, RTPMessage* msg);
int (*mcb) (void *, struct RTPMessage *msg);
} RTPSession;
/**
* Must be called before calling any other rtp function.
*/
RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) );
/**
* Terminate the session.
*/
void rtp_kill ( RTPSession* session );
/**
* Do periodical rtp work.
*/
int rtp_do(RTPSession *session);
/**
* By default rtp is in receiving state
*/
int rtp_start_receiving (RTPSession *session);
/**
* Pause rtp receiving mode.
*/
int rtp_stop_receiving (RTPSession *session);
/**
* Sends msg to RTPSession::dest
*/
int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy );
/**
* Dealloc msg.
*/
void rtp_free_msg ( RTPMessage *msg );
RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num,
BWControler *bwc, void *cs,
int (*mcb) (void *, struct RTPMessage *));
void rtp_kill (RTPSession *session);
int rtp_allow_receiving (RTPSession *session);
int rtp_stop_receiving (RTPSession *session);
int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length);
#endif /* RTP_H */

File diff suppressed because it is too large Load Diff

View File

@ -52,12 +52,19 @@ extern "C" {
/** \subsection threading Threading implications
*
* Unlike the Core API, this API is fully thread-safe. The library will ensure
* the proper synchronisation of parallel calls.
* the proper synchronization of parallel calls.
*
* A common way to run ToxAV (multiple or single instance) is to have a thread,
* separate from tox instance thread, running a simple toxav_iterate loop,
* sleeping for toxav_iteration_interval * milliseconds on each iteration.
*
* An important thing to note is that events are triggered from both tox and
* toxav thread (see above). Audio and video receive frame events are triggered
* from toxav thread while all the other events are triggered from tox thread.
*
* Tox thread has priority with mutex mechanisms. Any api function can
* fail if mutexes are held by tox thread in which case they will set SYNC
* error code.
*/
/**
* External Tox type.
@ -80,8 +87,10 @@ typedef struct Tox Tox;
*/
#ifndef TOXAV_DEFINED
#define TOXAV_DEFINED
typedef struct ToxAV ToxAV;
typedef struct ToxAV_s ToxAV;
#endif /* TOXAV_DEFINED */
/*******************************************************************************
*
* :: API version
@ -92,17 +101,20 @@ typedef struct ToxAV ToxAV;
* incompatible way.
*/
#define TOXAV_VERSION_MAJOR 0u
/**
* The minor version number. Incremented when functionality is added without
* breaking the API or ABI. Set to 0 when the major version number is
* incremented.
*/
#define TOXAV_VERSION_MINOR 0u
/**
* The patch or revision number. Incremented when bugfixes are applied without
* changing any functionality or API or ABI.
*/
#define TOXAV_VERSION_PATCH 0u
/**
* A macro to check at preprocessing time whether the client code is compatible
* with the installed version of ToxAV.
@ -112,37 +124,45 @@ typedef struct ToxAV ToxAV;
(TOXAV_VERSION_MINOR > MINOR || \
(TOXAV_VERSION_MINOR == MINOR && \
TOXAV_VERSION_PATCH >= PATCH)))
/**
* A macro to make compilation fail if the client code is not compatible with
* the installed version of ToxAV.
*/
#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
/**
* A convenience macro to call toxav_version_is_compatible with the currently
* compiling API version.
*/
#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
/**
* Return the major version number of the library. Can be used to display the
* ToxAV library version or to check whether the client is compatible with the
* dynamically linked version of ToxAV.
*/
uint32_t toxav_version_major(void);
/**
* Return the minor version number of the library.
*/
uint32_t toxav_version_minor(void);
/**
* Return the patch number of the library.
*/
uint32_t toxav_version_patch(void);
/**
* Return whether the compiled library version is compatible with the passed
* version numbers.
*/
bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
/*******************************************************************************
*
* :: Creation and destruction
@ -167,10 +187,12 @@ typedef enum TOXAV_ERR_NEW {
*/
TOXAV_ERR_NEW_MULTIPLE,
} TOXAV_ERR_NEW;
/**
* Start new A/V session. There can only be only one session per Tox instance.
*/
ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
/**
* Releases all resources associated with the A/V session.
*
@ -179,10 +201,13 @@ ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
* called and the av pointer becomes invalid.
*/
void toxav_kill(ToxAV *toxAV);
/**
* Returns the Tox instance the A/V object was created for.
*/
Tox *toxav_get_tox(const ToxAV *toxAV);
/*******************************************************************************
*
* :: A/V event loop
@ -193,12 +218,15 @@ Tox *toxav_get_tox(const ToxAV *toxAV);
* be. If no call is active at the moment, this function returns 200.
*/
uint32_t toxav_iteration_interval(const ToxAV *toxAV);
/**
* Main loop for the session. This function needs to be called in intervals of
* toxav_iteration_interval() milliseconds. It is best called in the separate
* thread from tox_iterate.
*/
void toxav_iterate(ToxAV *toxAV);
/*******************************************************************************
*
* :: Call setup
@ -214,6 +242,10 @@ typedef enum TOXAV_ERR_CALL {
* required for the call.
*/
TOXAV_ERR_CALL_MALLOC,
/**
* Synchronization error occurred.
*/
TOXAV_ERR_CALL_SYNC,
/**
* The friend number did not designate a valid friend.
*/
@ -232,6 +264,7 @@ typedef enum TOXAV_ERR_CALL {
*/
TOXAV_ERR_CALL_INVALID_BIT_RATE,
} TOXAV_ERR_CALL;
/**
* Call a friend. This will start ringing the friend.
*
@ -246,7 +279,9 @@ typedef enum TOXAV_ERR_CALL {
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
* video sending.
*/
bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
/**
* The function type for the call callback.
*
@ -254,17 +289,24 @@ bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, u
* @param audio_enabled True if friend is sending audio.
* @param video_enabled True if friend is sending video.
*/
typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data);
typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled,
bool video_enabled, void *user_data);
/**
* Set the callback for the `call` event. Pass NULL to unset.
*
*/
void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
typedef enum TOXAV_ERR_ANSWER {
/**
* The function returned successfully.
*/
TOXAV_ERR_ANSWER_OK,
/**
* Synchronization error occurred.
*/
TOXAV_ERR_ANSWER_SYNC,
/**
* Failed to initialize codecs for call session. Note that codec initiation
* will fail if there is no receive callback registered for either audio or
@ -285,6 +327,7 @@ typedef enum TOXAV_ERR_ANSWER {
*/
TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
} TOXAV_ERR_ANSWER;
/**
* Accept an incoming call.
*
@ -299,6 +342,8 @@ typedef enum TOXAV_ERR_ANSWER {
* video sending.
*/
bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error);
/*******************************************************************************
*
* :: Call state graph
@ -336,7 +381,6 @@ enum TOXAV_FRIEND_CALL_STATE {
TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
};
/**
* The function type for the call_state callback.
*
@ -347,11 +391,13 @@ enum TOXAV_FRIEND_CALL_STATE {
* friend.
*/
typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
/**
* Set the callback for the `call_state` event. Pass NULL to unset.
*
*/
void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
/*******************************************************************************
*
* :: Call control
@ -393,11 +439,16 @@ typedef enum TOXAV_CALL_CONTROL {
*/
TOXAV_CALL_CONTROL_SHOW_VIDEO,
} TOXAV_CALL_CONTROL;
typedef enum TOXAV_ERR_CALL_CONTROL {
/**
* The function returned successfully.
*/
TOXAV_ERR_CALL_CONTROL_OK,
/**
* Synchronization error occurred.
*/
TOXAV_ERR_CALL_CONTROL_SYNC,
/**
* The friend_number passed did not designate a valid friend.
*/
@ -413,6 +464,7 @@ typedef enum TOXAV_ERR_CALL_CONTROL {
*/
TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
} TOXAV_ERR_CALL_CONTROL;
/**
* Sends a call control command to a friend.
*
@ -423,48 +475,40 @@ typedef enum TOXAV_ERR_CALL_CONTROL {
* @return true on success.
*/
bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error);
/*******************************************************************************
*
* :: Controlling bit rates
*
******************************************************************************/
typedef enum TOXAV_ERR_SET_BIT_RATE {
typedef enum TOXAV_ERR_BIT_RATE_SET {
/**
* The function returned successfully.
*/
TOXAV_ERR_SET_BIT_RATE_OK,
TOXAV_ERR_BIT_RATE_SET_OK,
/**
* The bit rate passed was not one of the supported values.
* Synchronization error occurred.
*/
TOXAV_ERR_SET_BIT_RATE_INVALID,
TOXAV_ERR_BIT_RATE_SET_SYNC,
/**
* The audio bit rate passed was not one of the supported values.
*/
TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
/**
* The video bit rate passed was not one of the supported values.
*/
TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
/**
* The friend_number passed did not designate a valid friend.
*/
TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND,
TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
/**
* This client is currently not in a call with the friend.
*/
TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL,
} TOXAV_ERR_SET_BIT_RATE;
/**
* The function type for the audio_bit_rate_status callback.
*
* @param friend_number The friend number of the friend for which to set the
* audio bit rate.
* @param stable Is the stream stable enough to keep the bit rate.
* Upon successful, non forceful, bit rate change, this is set to
* true and 'bit_rate' is set to new bit rate.
* The stable is set to false with bit_rate set to the unstable
* bit rate when either current stream is unstable with said bit rate
* or the non forceful change failed.
* @param bit_rate The bit rate in Kb/sec.
*/
typedef void toxav_audio_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data);
/**
* Set the callback for the `audio_bit_rate_status` event. Pass NULL to unset.
*
*/
void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_status_cb *callback, void *user_data);
TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
} TOXAV_ERR_BIT_RATE_SET;
/**
* Set the audio bit rate to be used in subsequent audio frames. If the passed
* bit rate is the same as the current bit rate this function will return true
@ -476,46 +520,33 @@ void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_sta
* @param friend_number The friend number of the friend for which to set the
* audio bit rate.
* @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
* audio sending.
* @param force True if the bit rate change is forceful.
*
*/
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);
/**
* The function type for the video_bit_rate_status callback.
*
* @param friend_number The friend number of the friend for which to set the
* video bit rate.
* @param stable Is the stream stable enough to keep the bit rate.
* Upon successful, non forceful, bit rate change, this is set to
* true and 'bit_rate' is set to new bit rate.
* The stable is set to false with bit_rate set to the unstable
* bit rate when either current stream is unstable with said bit rate
* or the non forceful change failed.
* @param bit_rate The bit rate in Kb/sec.
*/
typedef void toxav_video_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data);
/**
* Set the callback for the `video_bit_rate_status` event. Pass NULL to unset.
*
*/
void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_status_cb *callback, void *user_data);
/**
* Set the video bit rate to be used in subsequent video frames. If the passed
* bit rate is the same as the current bit rate this function will return true
* without calling a callback. If there is an active non forceful setup with the
* passed video bit rate and the new set request is forceful, the bit rate is
* forcefully set and the previous non forceful request is cancelled. The active
* non forceful setup will be canceled in favour of new non forceful setup.
*
* @param friend_number The friend number of the friend for which to set the
* video bit rate.
* audio sending. Set to -1 to leave unchanged.
* @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
* video sending.
* @param force True if the bit rate change is forceful.
* video sending. Set to -1 to leave unchanged.
*
*/
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);
bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
/**
* The function type for the bit_rate_status callback. The event is triggered
* when the network becomes too saturated for current bit rates at which
* point core suggests new bit rates.
*
* @param friend_number The friend number of the friend for which to set the
* audio bit rate.
* @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
* @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
*/
typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data);
/**
* Set the callback for the `bit_rate_status` event. Pass NULL to unset.
*
*/
void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
/*******************************************************************************
*
* :: A/V sending
@ -554,6 +585,7 @@ typedef enum TOXAV_ERR_SEND_FRAME {
*/
TOXAV_ERR_SEND_FRAME_RTP_FAILED,
} TOXAV_ERR_SEND_FRAME;
/**
* Send an audio frame to a friend.
*
@ -574,7 +606,10 @@ typedef enum TOXAV_ERR_SEND_FRAME {
* @param sampling_rate Audio sampling rate used in this frame. Valid sampling
* rates are 8000, 12000, 16000, 24000, or 48000.
*/
bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error);
bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
size_t sample_count, uint8_t channels, uint32_t sampling_rate,
TOXAV_ERR_SEND_FRAME *error);
/**
* Send a video frame to a friend.
*
@ -590,7 +625,11 @@ bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t
* @param u U (Chroma) plane data.
* @param v V (Chroma) plane data.
*/
bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error);
bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
TOXAV_ERR_SEND_FRAME *error);
/*******************************************************************************
*
* :: A/V receiving
@ -600,7 +639,7 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width
* The function type for the audio_receive_frame callback. The callback can be
* called multiple times per single iteration depending on the amount of queued
* frames in the buffer. The received format is the same as in send function.
*
*
* @param friend_number The friend number of the friend who sent an audio frame.
* @param pcm An array of audio samples (sample_count * channels elements).
* @param sample_count The number of audio samples per channel in the PCM array.
@ -608,12 +647,16 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width
* @param sampling_rate Sampling rate used in this frame.
*
*/
typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void *user_data);
typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
size_t sample_count, uint8_t channels, uint32_t sampling_rate,
void *user_data);
/**
* Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
*
*/
void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
/**
* The function type for the video_receive_frame callback.
*
@ -635,60 +678,17 @@ void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_
* image is bottom-up hence why you MUST abs() it when
* calculating plane buffer size.
*/
typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
/**
* Set the callback for the `video_receive_frame` event. Pass NULL to unset.
*
*/
void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
/**
* NOTE Compatibility with old toxav group calls TODO remove
*/
/* Create a new toxav group.
*
* return group number on success.
* return -1 on failure.
*
* Audio data callback format:
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t,
unsigned int, void *), void *userdata);
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
/* Send audio to the group chat.
*
* return 0 on success.
* return -1 on failure.
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*
* Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
* Valid number of channels are 1 or 2.
* Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
*
* Recommended values are: samples = 960, channels = 1, sample_rate = 48000
*/
int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
unsigned int sample_rate);
#ifdef __cplusplus
}
#endif
#endif /* TOXAV_H */

View File

@ -1,5 +1,5 @@
/* toxav_old.h
*
*
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
*
* This file is part of Tox.
@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* This file contains the group chats code for the backwards compatibility.

View File

@ -19,6 +19,10 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#include <assert.h>
@ -29,281 +33,166 @@
#include "../toxcore/logger.h"
#include "../toxcore/network.h"
/* Good quality encode. */
#define MAX_DECODE_TIME_US 0
#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
#define VIDEOFRAME_HEADER_SIZE 0x2
#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
#define VIDEO_DECODE_BUFFER_SIZE 20
typedef struct { uint16_t size; uint8_t data[]; } Payload;
bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate);
bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate);
VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data, uint32_t mvfpsz)
VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
{
VCSession *vc = calloc(sizeof(VCSession), 1);
if (!vc) {
LOGGER_WARNING("Allocation failed! Application might misbehave!");
return NULL;
}
if (create_recursive_mutex(vc->queue_mutex) != 0) {
LOGGER_WARNING("Failed to create recursive mutex!");
free(vc);
return NULL;
}
if ( !(vc->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) )
if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)))
goto BASE_CLEANUP;
if ( !(vc->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) )
goto BASE_CLEANUP;
if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) )
goto BASE_CLEANUP;
int rc = vpx_codec_dec_init_ver(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE,
NULL, 0, VPX_DECODER_ABI_VERSION);
if ( rc != VPX_CODEC_OK) {
int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0);
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
goto BASE_CLEANUP;
}
if (!create_video_encoder(vc->encoder, 500000)) {
vpx_codec_destroy(vc->decoder);
goto BASE_CLEANUP;
}
if (!create_video_encoder(vc->test_encoder, 500000)) {
vpx_codec_destroy(vc->encoder);
vpx_codec_destroy(vc->decoder);
goto BASE_CLEANUP;
}
vc->linfts = current_time_monotonic();
vc->lcfd = 60;
vc->vcb.first = cb;
vc->vcb.second = cb_data;
vc->friend_number = friend_number;
vc->peer_video_frame_piece_size = mvfpsz;
vc->av = av;
return vc;
BASE_CLEANUP:
pthread_mutex_destroy(vc->queue_mutex);
rb_free(vc->vbuf_raw);
free(vc->split_video_frame);
free(vc->frame_buf);
rb_kill(vc->vbuf_raw);
free(vc);
return NULL;
}
void vc_kill(VCSession* vc)
void vc_kill(VCSession *vc)
{
if (!vc)
return;
vpx_codec_destroy(vc->encoder);
vpx_codec_destroy(vc->test_encoder);
vpx_codec_destroy(vc->decoder);
rb_free(vc->vbuf_raw);
free(vc->split_video_frame);
free(vc->frame_buf);
void *p;
while (rb_read(vc->vbuf_raw, (void **)&p))
free(p);
rb_kill(vc->vbuf_raw);
pthread_mutex_destroy(vc->queue_mutex);
LOGGER_DEBUG("Terminated video handler: %p", vc);
free(vc);
}
void vc_do(VCSession* vc)
void vc_iterate(VCSession *vc)
{
if (!vc)
return;
Payload *p;
struct RTPMessage *p;
int rc;
pthread_mutex_lock(vc->queue_mutex);
if (rb_read(vc->vbuf_raw, (void**)&p)) {
if (rb_read(vc->vbuf_raw, (void **)&p)) {
pthread_mutex_unlock(vc->queue_mutex);
rc = vpx_codec_decode(vc->decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
rc = vpx_codec_decode(vc->decoder, p->data, p->len, NULL, MAX_DECODE_TIME_US);
free(p);
if (rc != VPX_CODEC_OK) {
if (rc != VPX_CODEC_OK)
LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
} else {
else {
vpx_codec_iter_t iter = NULL;
vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
/* Play decoded images */
for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
if (vc->vcb.first)
vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
(const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2],
if (vc->vcb.first)
vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
(const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
vpx_img_free(dest);
}
}
return;
}
pthread_mutex_unlock(vc->queue_mutex);
}
void vc_init_video_splitter_cycle(VCSession* vc)
int vc_queue_message(void *vcp, struct RTPMessage *msg)
{
if (!vc)
return;
vc->split_video_frame[0] = vc->frameid_out++;
vc->split_video_frame[1] = 0;
}
int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length)
{
if (!vc)
return 0;
vc->processing_video_frame = payload;
vc->processing_video_frame_size = length;
return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1;
}
const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size)
{
if (!vc || !size)
return NULL;
if (vc->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) {
memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
vc->processing_video_frame,
VIDEOFRAME_PIECE_SIZE);
vc->processing_video_frame += VIDEOFRAME_PIECE_SIZE;
vc->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE;
*size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE;
} else {
memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
vc->processing_video_frame,
vc->processing_video_frame_size);
*size = vc->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
}
vc->split_video_frame[1]++;
return vc->split_video_frame;
}
int vc_queue_message(void* vcp, struct RTPMessage_s *msg)
{
/* This function does the reconstruction of video packets.
/* This function does the reconstruction of video packets.
* See more info about video splitting in docs
*/
if (!vcp || !msg)
return -1;
if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeVideo + 2) % 128) {
if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
LOGGER_WARNING("Got dummy!");
rtp_free_msg(msg);
free(msg);
return 0;
}
if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) {
if (msg->header.pt != rtp_TypeVideo % 128) {
LOGGER_WARNING("Invalid payload type!");
rtp_free_msg(msg);
free(msg);
return -1;
}
VCSession* vc = vcp;
uint8_t *packet = msg->data;
uint32_t packet_size = msg->length;
if (packet_size < VIDEOFRAME_HEADER_SIZE)
goto end;
VCSession *vc = vcp;
uint8_t diff = packet[0] - vc->frameid_in;
if (diff != 0) {
if (diff < 225) { /* New frame */
/* Flush last frames' data and get ready for this frame */
Payload *p = malloc(sizeof(Payload) + vc->frame_size);
if (p) {
pthread_mutex_lock(vc->queue_mutex);
if (rb_full(vc->vbuf_raw)) {
LOGGER_DEBUG("Dropped video frame");
Payload *tp;
rb_read(vc->vbuf_raw, (void**)&tp);
free(tp);
} else {
p->size = vc->frame_size;
memcpy(p->data, vc->frame_buf, vc->frame_size);
}
/* Calculate time took for peer to send us this frame */
uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
vc->linfts = current_time_monotonic();
rb_write(vc->vbuf_raw, p);
pthread_mutex_unlock(vc->queue_mutex);
} else {
LOGGER_WARNING("Allocation failed! Program might misbehave!");
goto end;
}
vc->frameid_in = packet[0];
memset(vc->frame_buf, 0, vc->frame_size);
vc->frame_size = 0;
} else { /* Old frame; drop */
LOGGER_DEBUG("Old packet: %u", packet[0]);
goto end;
}
pthread_mutex_lock(vc->queue_mutex);
free(rb_write(vc->vbuf_raw, msg));
{
/* Calculate time took for peer to send us this frame */
uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
vc->linfts = current_time_monotonic();
}
pthread_mutex_unlock(vc->queue_mutex);
uint8_t piece_number = packet[1];
uint32_t length_before_piece = ((piece_number - 1) * vc->peer_video_frame_piece_size);
uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
if (framebuf_new_length > MAX_VIDEOFRAME_SIZE)
goto end;
/* Otherwise it's part of the frame so just process */
/* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
memcpy(vc->frame_buf + length_before_piece,
packet + VIDEOFRAME_HEADER_SIZE,
packet_size - VIDEOFRAME_HEADER_SIZE);
if (framebuf_new_length > vc->frame_size)
vc->frame_size = framebuf_new_length;
end:
rtp_free_msg(msg);
return 0;
}
int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height)
int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height)
{
if (!vccdc)
return -1;
vpx_codec_enc_cfg_t cfg = *vccdc->config.enc;
if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height)
return 0; /* Nothing changed */
cfg.rc_target_bitrate = bit_rate;
cfg.g_w = width;
cfg.g_h = height;
int rc = vpx_codec_enc_config_set(vccdc, &cfg);
if ( rc != VPX_CODEC_OK) {
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
return -1;
}
@ -312,42 +201,43 @@ int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t w
}
bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate)
bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate)
{
assert(dest);
vpx_codec_enc_cfg_t cfg;
int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
return false;
}
cfg.rc_target_bitrate = bit_rate;
cfg.g_w = 4000;
cfg.g_h = 4000;
cfg.g_w = 800;
cfg.g_h = 600;
cfg.g_pass = VPX_RC_ONE_PASS;
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
/* FIXME If we set error resilience the app will crash due to bug in vp8.
Perhaps vp9 has solved it?*/
// cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
cfg.g_lag_in_frames = 0;
cfg.kf_min_dist = 0;
cfg.kf_max_dist = 48;
cfg.kf_mode = VPX_KF_AUTO;
rc = vpx_codec_enc_init_ver(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0,
VPX_ENCODER_ABI_VERSION);
if ( rc != VPX_CODEC_OK) {
rc = vpx_codec_enc_init(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
return false;
}
rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8);
if ( rc != VPX_CODEC_OK) {
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
vpx_codec_destroy(dest);
}
return true;
}
}

View File

@ -36,77 +36,32 @@
#include "../toxcore/util.h"
struct RTPMessage_s;
struct RTPMessage;
/*
* Base Video Codec session type.
*/
typedef struct VCSession_s {
/* encoding */
vpx_codec_ctx_t encoder[1];
vpx_codec_ctx_t test_encoder[1];
uint32_t frame_counter;
uint32_t test_frame_counter;
/* decoding */
vpx_codec_ctx_t decoder[1];
void *vbuf_raw; /* Un-decoded data */
void *vbuf_raw; /* Un-decoded data */
/* Data handling */
uint8_t *frame_buf; /* buffer for split video payloads */
uint32_t frame_size; /* largest address written to in frame_buf for current input frame */
uint8_t frameid_in, frameid_out; /* id of input and output video frame */
uint64_t linfts; /* Last received frame time stamp */
uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
/* Limits */
uint32_t peer_video_frame_piece_size;
/* Splitting */
uint8_t *split_video_frame;
const uint8_t *processing_video_frame;
uint16_t processing_video_frame_size;
ToxAV *av;
uint32_t friend_number;
PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
pthread_mutex_t queue_mutex[1];
} VCSession;
/*
* Create new Video Codec session.
*/
VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data, uint32_t mvfpsz);
/*
* Kill the Video Codec session.
*/
void vc_kill(VCSession* vc);
/*
* Do periodic work. Work is consisted out of decoding only.
*/
void vc_do(VCSession* vc);
/*
* Set new video splitting cycle. This is requirement in order to send video packets.
*/
void vc_init_video_splitter_cycle(VCSession* vc);
/*
* Update the video splitter cycle with new data.
*/
int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length);
/*
* Iterate over splitted cycle.
*/
const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size);
/*
* Queue new rtp message.
*/
int vc_queue_message(void *vcp, struct RTPMessage_s *msg);
/*
* Set new values to the encoders.
*/
int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height);
VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data);
void vc_kill(VCSession *vc);
void vc_iterate(VCSession *vc);
int vc_queue_message(void *vcp, struct RTPMessage *msg);
int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height);
#endif /* VIDEO_H */
#endif /* VIDEO_H */

View File

@ -2239,7 +2239,7 @@ static void connection_status_cb(Messenger *m)
}
#ifdef LOGGING
#ifdef TOX_LOGGER
#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
static time_t lastdump = 0;
static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1];
@ -2315,7 +2315,7 @@ void do_messenger(Messenger *m)
do_friends(m);
connection_status_cb(m);
#ifdef LOGGING
#ifdef TOX_LOGGER
if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
@ -2414,7 +2414,7 @@ void do_messenger(Messenger *m)
}
}
#endif /* LOGGING */
#endif /* TOX_LOGGER */
}
/* new messenger format for load/save, more robust and forward compatible */

View File

@ -878,9 +878,9 @@ void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id)
}
}
#ifdef LOGGING
#ifdef TOX_LOGGER
static char *idpart2str(uint8_t *id, size_t len);
#endif /* LOGGING */
#endif /* TOX_LOGGER */
/* refresh buckets */
void do_Assoc(Assoc *assoc, DHT *dht)
@ -974,7 +974,7 @@ void kill_Assoc(Assoc *assoc)
}
}
#ifdef LOGGING
#ifdef TOX_LOGGER
static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1];
static char *idpart2str(uint8_t *id, size_t len)
@ -1028,4 +1028,4 @@ void Assoc_status(const Assoc *assoc)
}
}
#endif /* LOGGING */
#endif /* TOX_LOGGER */

View File

@ -97,8 +97,8 @@ void do_Assoc(Assoc *assoc, DHT *dht);
/* destroy */
void kill_Assoc(Assoc *assoc);
#ifdef LOGGING
#ifdef TOX_LOGGER
void Assoc_status(const Assoc *assoc);
#endif /* LOGGING */
#endif /* TOX_LOGGER */
#endif /* !__ASSOC_H__ */

View File

@ -44,7 +44,7 @@
#endif
struct logger {
struct Logger {
FILE *log_file;
LOG_LEVEL level;
uint64_t start_time; /* Time when lib loaded */
@ -87,7 +87,7 @@ char *strtime(char *dest, size_t max_len)
*/
Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
{
#ifndef LOGGING /* Disabled */
#ifndef TOX_LOGGER /* Disabled */
return NULL;
#endif
@ -96,7 +96,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
if (!retu)
return NULL;
if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) {
if (pthread_mutex_init(retu->mutex, NULL) != 0) {
free(retu);
return NULL;
}
@ -110,7 +110,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
if (!(retu->tstr = calloc(16, sizeof (char))) ||
!(retu->posstr = calloc(300, sizeof (char))) ||
!(retu->msg = calloc(4096, sizeof (char))) )
!(retu->msg = calloc(4096, sizeof (char))))
goto FAILURE;
if (id) {
@ -147,7 +147,7 @@ FAILURE:
void logger_kill(Logger *log)
{
#ifndef LOGGING /* Disabled */
#ifndef TOX_LOGGER /* Disabled */
return;
#endif
@ -160,7 +160,7 @@ void logger_kill(Logger *log)
free(log->posstr);
free(log->msg);
if (fclose(log->log_file) != 0 )
if (fclose(log->log_file) != 0)
perror("Could not close log file");
pthread_mutex_unlock(log->mutex);
@ -177,7 +177,7 @@ void logger_kill_global(void)
void logger_set_global(Logger *log)
{
#ifndef LOGGING /* Disabled */
#ifndef TOX_LOGGER /* Disabled */
return;
#endif
@ -186,7 +186,7 @@ void logger_set_global(Logger *log)
Logger *logger_get_global(void)
{
#ifndef LOGGING /* Disabled */
#ifndef TOX_LOGGER /* Disabled */
return NULL;
#endif
@ -195,7 +195,7 @@ Logger *logger_get_global(void)
void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...)
{
#ifndef LOGGING /* Disabled */
#ifndef TOX_LOGGER /* Disabled */
return;
#endif

View File

@ -43,7 +43,7 @@ typedef enum {
LOG_ERROR
} LOG_LEVEL;
typedef struct logger Logger;
typedef struct Logger Logger;
/**
* Set 'level' as the lowest printable level. If id == NULL, random number is used.
@ -66,21 +66,22 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con
/* To do some checks or similar only when logging, use this */
#ifdef LOGGING
#ifdef TOX_LOGGER
# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)
# define LOGGER_WRITE(log, level, format, ...) \
logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__ )
logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__)
#else
/* # warning "Logging disabled" */
# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0)
# define LOGGER_WRITE(log, level, format, ...) do {} while(0)
#endif /* LOGGING */
#endif /* TOX_LOGGER */
/* To log with an logger */
#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__ )
#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__ )
#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__ )
#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__ )
#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__ )
#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__)
#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__)
#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__)
#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__)
#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__)
/* To log with the global logger */
#define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__)

View File

@ -266,7 +266,7 @@ uint64_t current_time_monotonic(void)
}
/* In case no logging */
#ifndef LOGGING
#ifndef TOX_LOGGER
#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__)
#else
#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0
@ -287,7 +287,7 @@ uint64_t current_time_monotonic(void)
__buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \
ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__));
#endif /* LOGGING */
#endif /* TOX_LOGGER */
/* Basic network functions:
* Function to send packet(data) of length length to ip_port.
@ -615,9 +615,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
}
if (ip.family == AF_INET6) {
#ifdef LOGGING
#ifdef TOX_LOGGER
int is_dualstack =
#endif /* LOGGING */
#endif /* TOX_LOGGER */
set_socket_dualstack(temp->sock);
LOGGER_DEBUG( "Dual-stack socket: %s",
is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" );
@ -628,9 +628,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02;
mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
mreq.ipv6mr_interface = 0;
#ifdef LOGGING
#ifdef TOX_LOGGER
int res =
#endif /* LOGGING */
#endif /* TOX_LOGGER */
setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" :

View File

@ -234,14 +234,6 @@ bool rb_read(RingBuffer *b, void **p)
b->start = (b->start + 1) % b->size;
return true;
}
void rb_clear(RingBuffer *b)
{
while (!rb_empty(b)) {
void *p;
rb_read(b, &p);
free(p);
}
}
RingBuffer *rb_new(int size)
{
RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
@ -257,11 +249,28 @@ RingBuffer *rb_new(int size)
return buf;
}
void rb_free(RingBuffer *b)
void rb_kill(RingBuffer *b)
{
if (b) {
rb_clear(b);
free(b->data);
free(b);
}
}
uint16_t rb_size(const RingBuffer* b)
{
if (rb_empty(b))
return 0;
return
b->end > b->start ?
b->end - b->start :
(b->size - b->start) + b->end;
}
uint16_t rb_data(const RingBuffer* b, void** dest)
{
uint16_t i = 0;
for (; i < rb_size(b); i++)
dest[i] = b->data[(b->start + i) % b->size];
return i;
}

View File

@ -64,7 +64,9 @@ bool rb_full(const RingBuffer *b);
bool rb_empty(const RingBuffer *b);
void* rb_write(RingBuffer* b, void* p);
bool rb_read(RingBuffer* b, void** p);
void rb_clear(RingBuffer *b);
RingBuffer *rb_new(int size);
void rb_free(RingBuffer *b);
void rb_kill(RingBuffer *b);
uint16_t rb_size(const RingBuffer *b);
uint16_t rb_data(const RingBuffer* b, void** dest);
#endif /* __UTIL_H__ */