This commit is contained in:
irungentoo 2014-11-24 20:24:59 -05:00
commit 279c33c01a
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
14 changed files with 1457 additions and 2177 deletions

View File

@ -11,6 +11,8 @@
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <vpx/vpx_image.h>
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/crypto_core.h" #include "../toxcore/crypto_core.h"
@ -33,7 +35,7 @@ typedef enum _CallStatus {
Ringing, Ringing,
Ended, Ended,
Rejected, Rejected,
Cancel, Canceled,
TimedOut TimedOut
} CallStatus; } CallStatus;
@ -66,36 +68,23 @@ void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
/* Bob always receives invite */ if (cast->Alice.av == av) {
cast->Bob.status = Ringing; // ...
cast->Bob.call_index = call_index; } else if (cast->Bob.av == av) {
/* Bob always receives invite */
cast->Bob.status = Ringing;
cast->Bob.call_index = call_index;
}
} }
void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
/* Alice always sends invite */ if (cast->Alice.av == av) {
cast->Alice.status = Ringing; /* Alice always sends invite */
} cast->Alice.status = Ringing;
void callback_recv_starting ( void *av, int32_t call_index, void *_arg ) } else if (cast->Bob.av == av) {
{ // ...
Status *cast = _arg;
/* Alice always sends invite */
printf("Call started on Alice side...\n");
cast->Alice.status = InCall;
toxav_prepare_transmission(av, call_index, av_jbufdc, av_VADd, 1);
}
void callback_recv_ending ( void *av, int32_t call_index, void *_arg )
{
Status *cast = _arg;
if ( cast->Alice.status == Rejected) {
printf ( "Call ended for Bob!\n" );
cast->Bob.status = Ended;
} else {
printf ( "Call ended for Alice!\n" );
cast->Alice.status = Ended;
} }
} }
@ -104,17 +93,26 @@ void callback_call_started ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
/* Alice always sends invite */ if (cast->Alice.av == av) {
printf("Call started on Bob side...\n"); printf("Call started on Alices side...\n");
cast->Bob.status = InCall; cast->Alice.status = InCall;
toxav_prepare_transmission(av, call_index, av_jbufdc, av_VADd, 1); toxav_prepare_transmission(av, call_index, 1);
} else if (cast->Bob.av == av) {
printf("Call started on Bob side...\n");
cast->Bob.status = InCall;
toxav_prepare_transmission(av, call_index, 1);
}
} }
void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
printf ( "Call Canceled for Bob!\n" ); if (cast->Alice.av == av) {
cast->Bob.status = Cancel; // ...
} else if (cast->Bob.av == av) {
printf ( "Call Canceled for Bob!\n" );
cast->Bob.status = Canceled;
}
} }
void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
{ {
@ -122,23 +120,58 @@ void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
printf ( "Call rejected by Bob!\n" printf ( "Call rejected by Bob!\n"
"Call ended for Alice!\n" ); "Call ended for Alice!\n" );
/* If Bob rejects, call is ended for alice and she sends ending */ /* If Bob rejects, call is ended for alice and she sends ending */
cast->Alice.status = Rejected; if (cast->Alice.av == av) {
cast->Alice.status = Rejected;
} else if (cast->Bob.av == av) {
//... ignor
}
} }
void callback_call_ended ( void *av, int32_t call_index, void *_arg ) void callback_call_ended ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
printf ( "Call ended for Bob!\n" ); if (cast->Alice.av == av) {
cast->Bob.status = Ended; printf ( "Call ended for Alice!\n" );
cast->Alice.status = Ended;
} else if (cast->Bob.av == av) {
printf ( "Call ended for Bob!\n" );
cast->Bob.status = Ended;
}
} }
void callback_call_type_change ( void *av, int32_t call_index, void *_arg ) void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg )
{ {
ToxAvCSettings csettings; ToxAvCSettings csettings;
toxav_get_peer_csettings(av, call_index, 0, &csettings); toxav_get_peer_csettings(av, call_index, 0, &csettings);
printf("New settings: \n" printf("Peer changing settings to: \n"
"Type: %u \n"
"Video bitrate: %u \n"
"Video height: %u \n"
"Video width: %u \n"
"Audio bitrate: %u \n"
"Audio framedur: %u \n"
"Audio sample rate: %u \n"
"Audio channels: %u \n",
csettings.call_type,
csettings.video_bitrate,
csettings.max_video_height,
csettings.max_video_width,
csettings.audio_bitrate,
csettings.audio_frame_duration,
csettings.audio_sample_rate,
csettings.audio_channels
);
}
void callback_self_cs_change ( void *av, int32_t call_index, void *_arg )
{
ToxAvCSettings csettings;
toxav_get_peer_csettings(av, call_index, 0, &csettings);
printf("Changed settings to: \n"
"Type: %u \n" "Type: %u \n"
"Video bitrate: %u \n" "Video bitrate: %u \n"
"Video height: %u \n" "Video height: %u \n"
@ -162,16 +195,19 @@ void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
printf("Call timed-out!\n"); printf("Call timed-out!\n");
cast->Alice.status = TimedOut;
if (cast->Alice.av == av) {
cast->Alice.status = TimedOut;
} else if (cast->Bob.av == av) {
cast->Bob.status = TimedOut;
}
} }
static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length, void *userdata) void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
{ {}
}
static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img, void *userdata) void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
{ {}
}
void register_callbacks(ToxAv *av, void *data) void register_callbacks(ToxAv *av, void *data)
{ {
@ -180,17 +216,12 @@ void register_callbacks(ToxAv *av, void *data)
toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data);
toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data);
toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data);
toxav_register_callstate_callback(av, callback_recv_starting, av_OnStarting, data);
toxav_register_callstate_callback(av, callback_recv_ending, av_OnEnding, data);
toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
toxav_register_callstate_callback(av, callback_call_type_change, av_OnMediaChange, data); toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data);
toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data);
toxav_register_audio_callback(callback_audio, NULL);
toxav_register_audio_recv_callback(av, callback_audio, NULL); toxav_register_video_callback(callback_video, NULL);
toxav_register_video_recv_callback(av, callback_video, NULL);
} }
@ -202,6 +233,7 @@ void register_callbacks(ToxAv *av, void *data)
#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \ #define CALL_AND_START_LOOP(AliceCallType, BobCallType) \
{ int step = 0, running = 1; while (running) {\ { int step = 0, running = 1; while (running) {\
tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); \ tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); \
toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \
switch ( step ) {\ switch ( step ) {\
case 0: /* Alice */ printf("Alice is calling...\n");\ case 0: /* Alice */ printf("Alice is calling...\n");\
toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); step++; break;\ toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); step++; break;\
@ -496,6 +528,10 @@ START_TEST(test_AV_flows)
tox_do(Alice); tox_do(Alice);
tox_do(Bob); tox_do(Bob);
toxav_do(status_control.Alice.av);
toxav_do(status_control.Bob.av);
switch ( step ) { switch ( step ) {
case 0: /* Alice */ case 0: /* Alice */
printf("Alice is calling...\n"); printf("Alice is calling...\n");
@ -514,7 +550,7 @@ START_TEST(test_AV_flows)
break; break;
case 2: /* Wait for Both to have status ended */ case 2: /* Wait for Both to have status ended */
if (status_control.Bob.status == Cancel) running = 0; if (status_control.Bob.status == Canceled) running = 0;
break; break;
} }
@ -537,6 +573,9 @@ START_TEST(test_AV_flows)
tox_do(Alice); tox_do(Alice);
tox_do(Bob); tox_do(Bob);
toxav_do(status_control.Alice.av);
toxav_do(status_control.Bob.av);
switch ( step ) { switch ( step ) {
case 0: case 0:
printf("Alice is calling...\n"); printf("Alice is calling...\n");

View File

@ -11,6 +11,8 @@
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <vpx/vpx_image.h>
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/crypto_core.h" #include "../toxcore/crypto_core.h"
@ -32,7 +34,7 @@ typedef enum _CallStatus {
Ringing, Ringing,
Ended, Ended,
Rejected, Rejected,
Cancel Canceled
} CallStatus; } CallStatus;
@ -67,66 +69,51 @@ void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *dat
/******************************************************************************/ /******************************************************************************/
void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
{ {
/* Status *cast = _arg;
Status *cast = _arg; cast->calls[call_index].Callee.status = Ringing;
cast->calls[call_index].Callee.status = Ringing;*/
} }
void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
cast->calls[call_index].Caller.status = Ringing; cast->calls[call_index].Caller.status = Ringing;
} }
void callback_recv_starting ( void *av, int32_t call_index, void *_arg ) void callback_call_ended ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
cast->calls[call_index].Caller.status = InCall;
}
void callback_recv_ending ( void *av, int32_t call_index, void *_arg )
{
Status *cast = _arg;
cast->calls[call_index].Caller.status = Ended;
}
if (av == cast->calls[call_index].Caller.av)
cast->calls[call_index].Caller.status = Ended;
else
cast->calls[call_index].Callee.status = Ended;
}
void callback_call_started ( void *av, int32_t call_index, void *_arg ) void callback_call_started ( void *av, int32_t call_index, void *_arg )
{ {
/* Status *cast = _arg;
Status *cast = _arg;
cast->calls[call_index].Callee.status = InCall;*/ if (av == cast->calls[call_index].Caller.av)
cast->calls[call_index].Caller.status = InCall;
else
cast->calls[call_index].Callee.status = InCall;
} }
void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
{ {
/*
Status *cast = _arg;
cast->calls[call_index].Callee.status = Cancel;*/
} }
void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
{ {
Status *cast = _arg; Status *cast = _arg;
cast->calls[call_index].Caller.status = Rejected; cast->calls[call_index].Caller.status = Rejected;
} }
void callback_call_ended ( void *av, int32_t call_index, void *_arg )
{
/*
Status *cast = _arg;
cast->calls[call_index].Callee.status = Ended;*/
}
void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
{ {
//ck_assert_msg(0, "No answer!"); ck_assert_msg(0, "No answer!");
} }
static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length, void *userdata) void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
{ {}
}
static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img, void *userdata) void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
{ {}
}
void register_callbacks(ToxAv *av, void *data) void register_callbacks(ToxAv *av, void *data)
{ {
@ -135,16 +122,12 @@ void register_callbacks(ToxAv *av, void *data)
toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data);
toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data);
toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data);
toxav_register_callstate_callback(av, callback_recv_starting, av_OnStarting, data);
toxav_register_callstate_callback(av, callback_recv_ending, av_OnEnding, data);
toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
toxav_register_audio_recv_callback(av, callback_audio, NULL); toxav_register_audio_callback(callback_audio, NULL);
toxav_register_video_recv_callback(av, callback_video, NULL); toxav_register_video_callback(callback_video, NULL);
} }
/*************************************************************************************************/ /*************************************************************************************************/
@ -193,14 +176,14 @@ void *in_thread_call (void *arg)
call_print(call_idx, "Sending rtp ..."); call_print(call_idx, "Sending rtp ...");
c_sleep(1000); /* We have race condition here */ c_sleep(1000); /* We have race condition here */
toxav_prepare_transmission(this_call->Callee.av, 0, 3, 0, 1); toxav_prepare_transmission(this_call->Callee.av, 0, 1);
toxav_prepare_transmission(this_call->Caller.av, call_idx, 3, 0, 1); toxav_prepare_transmission(this_call->Caller.av, call_idx, 1);
int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE, int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE,
sample_payload, frame_size); sample_payload, frame_size);
if ( payload_size < 0 ) { if ( payload_size < 0 ) {
//ck_assert_msg ( 0, "Failed to encode payload" ); ck_assert_msg ( 0, "Failed to encode payload" );
} }
@ -253,8 +236,8 @@ void *in_thread_call (void *arg)
// START_TEST(test_AV_three_calls) START_TEST(test_AV_three_calls)
void test_AV_three_calls() // void test_AV_three_calls()
{ {
long long unsigned int cur_time = time(NULL); long long unsigned int cur_time = time(NULL);
Tox *bootstrap_node = tox_new(0); Tox *bootstrap_node = tox_new(0);
@ -266,12 +249,12 @@ void test_AV_three_calls()
}; };
//ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node"); ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node");
int i = 0; int i = 0;
for (; i < 3; i ++) { for (; i < 3; i ++) {
//ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances"); ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances");
} }
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
@ -281,7 +264,7 @@ void test_AV_three_calls()
tox_get_address(callees[i], address); tox_get_address(callees[i], address);
int test = tox_add_friend(caller, address, (uint8_t *)"gentoo", 7); int test = tox_add_friend(caller, address, (uint8_t *)"gentoo", 7);
//ck_assert_msg( test == i, "Failed to add friend error code: %i", test); ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
} }
uint8_t off = 1; uint8_t off = 1;
@ -350,6 +333,13 @@ void test_AV_three_calls()
tox_do(callees[1]); tox_do(callees[1]);
tox_do(callees[2]); tox_do(callees[2]);
for ( i = 0; i < 3; i++ )
toxav_do(status_control.calls[0].Caller.av);
toxav_do(status_control.calls[0].Callee.av);
toxav_do(status_control.calls[1].Callee.av);
toxav_do(status_control.calls[2].Callee.av);
pthread_mutex_unlock(&muhmutex); pthread_mutex_unlock(&muhmutex);
c_sleep(20); c_sleep(20);
} }
@ -366,7 +356,7 @@ void test_AV_three_calls()
tox_kill(callees[i]); tox_kill(callees[i]);
} }
// END_TEST END_TEST
@ -382,21 +372,22 @@ Suite *tox_suite(void)
return s; return s;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Suite *tox = tox_suite(); Suite *tox = tox_suite();
// SRunner *test_runner = srunner_create(tox); SRunner *test_runner = srunner_create(tox);
//
// setbuf(stdout, NULL);
//
// srunner_run_all(test_runner, CK_NORMAL);
// int number_failed = srunner_ntests_failed(test_runner);
//
// srunner_free(test_runner);
//
// return number_failed;
test_AV_three_calls(); setbuf(stdout, NULL);
return 0; srunner_run_all(test_runner, CK_NORMAL);
int number_failed = srunner_ntests_failed(test_runner);
srunner_free(test_runner);
return number_failed;
// test_AV_three_calls();
// return 0;
} }

View File

@ -32,11 +32,107 @@
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
#include <time.h>
#include "msi.h"
#include "rtp.h" #include "rtp.h"
#include "codec.h" #include "codec.h"
JitterBuffer *create_queue(unsigned int capacity) /* Assume 24 fps*/
#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
#define MAX_DECODE_TIME_US 0
// TODO this has to be exchanged in msi
#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/
#define VIDEOFRAME_HEADER_SIZE 0x2
/* FIXME: Might not be enough */
#define VIDEO_DECODE_BUFFER_SIZE 20
#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; }
#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
typedef ARRAY(uint8_t) Payload;
static PAIR(CSVideoCallback, void *) vpcallback;
static PAIR(CSAudioCallback, void *) apcallback;
typedef struct {
uint16_t size; /* Max size */
uint16_t start;
uint16_t end;
Payload **packets;
} PayloadBuffer;
static _Bool buffer_full(const PayloadBuffer *b)
{
return (b->end + 1) % b->size == b->start;
}
static _Bool buffer_empty(const PayloadBuffer *b)
{
return b->end == b->start;
}
static void buffer_write(PayloadBuffer *b, Payload *p)
{
b->packets[b->end] = p;
b->end = (b->end + 1) % b->size;
if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */
}
static void buffer_read(PayloadBuffer *b, Payload **p)
{
*p = b->packets[b->start];
b->start = (b->start + 1) % b->size;
}
static void buffer_clear(PayloadBuffer *b)
{
while (!buffer_empty(b)) {
Payload *p;
buffer_read(b, &p);
free(p);
}
}
static PayloadBuffer *buffer_new(int size)
{
PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1);
if (!buf) return NULL;
buf->size = size + 1; /* include empty elem */
if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) {
free(buf);
return NULL;
}
return buf;
}
static void buffer_free(PayloadBuffer *b)
{
if (b) {
buffer_clear(b);
free(b->packets);
free(b);
}
}
/* JITTER BUFFER WORK */
typedef struct _JitterBuffer {
RTPMessage **queue;
uint32_t size;
uint32_t capacity;
uint16_t bottom;
uint16_t top;
} JitterBuffer;
static JitterBuffer *jbuf_new(uint32_t capacity)
{ {
unsigned int size = 1; unsigned int size = 1;
@ -58,7 +154,7 @@ JitterBuffer *create_queue(unsigned int capacity)
return q; return q;
} }
static void clear_queue(JitterBuffer *q) static void jbuf_clear(JitterBuffer *q)
{ {
for (; q->bottom != q->top; ++q->bottom) { for (; q->bottom != q->top; ++q->bottom) {
if (q->queue[q->bottom % q->size]) { if (q->queue[q->bottom % q->size]) {
@ -68,25 +164,25 @@ static void clear_queue(JitterBuffer *q)
} }
} }
void terminate_queue(JitterBuffer *q) static void jbuf_free(JitterBuffer *q)
{ {
if (!q) return; if (!q) return;
clear_queue(q); jbuf_clear(q);
free(q->queue); free(q->queue);
free(q); free(q);
} }
void queue(JitterBuffer *q, RTPMessage *pk) static void jbuf_write(JitterBuffer *q, RTPMessage *m)
{ {
uint16_t sequnum = pk->header->sequnum; uint16_t sequnum = m->header->sequnum;
unsigned int num = sequnum % q->size; unsigned int num = sequnum % q->size;
if ((uint32_t)(sequnum - q->bottom) > q->size) { if ((uint32_t)(sequnum - q->bottom) > q->size) {
clear_queue(q); jbuf_clear(q);
q->bottom = sequnum; q->bottom = sequnum;
q->queue[num] = pk; q->queue[num] = m;
q->top = sequnum + 1; q->top = sequnum + 1;
return; return;
} }
@ -94,14 +190,16 @@ void queue(JitterBuffer *q, RTPMessage *pk)
if (q->queue[num]) if (q->queue[num])
return; return;
q->queue[num] = pk; q->queue[num] = m;
if ((sequnum - q->bottom) >= (q->top - q->bottom)) if ((sequnum - q->bottom) >= (q->top - q->bottom))
q->top = sequnum + 1; q->top = sequnum + 1;
} }
/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ /* Success is 0 when there is nothing to dequeue,
RTPMessage *dequeue(JitterBuffer *q, int *success) * 1 when there's a good packet,
* 2 when there's a lost packet */
static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success)
{ {
if (q->top == q->bottom) { if (q->top == q->bottom) {
*success = 0; *success = 0;
@ -128,8 +226,7 @@ RTPMessage *dequeue(JitterBuffer *q, int *success)
return NULL; return NULL;
} }
static int init_video_decoder(CSSession *cs)
int init_video_decoder(CodecState *cs)
{ {
int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION);
@ -141,21 +238,230 @@ int init_video_decoder(CodecState *cs)
return 0; return 0;
} }
int init_audio_decoder(CodecState *cs, uint32_t audio_channels) static int init_audio_decoder(CSSession *cs)
{ {
int rc; int rc;
cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc );
if ( rc != OPUS_OK ) { if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
return -1; return -1;
} }
cs->audio_decoder_channels = audio_channels;
return 0; return 0;
} }
int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height) static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate)
{
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 -1;
}
cfg.rc_target_bitrate = video_bitrate;
cfg.g_w = max_width;
cfg.g_h = max_height;
cfg.g_pass = VPX_RC_ONE_PASS;
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
cfg.g_lag_in_frames = 0;
cfg.kf_min_dist = 0;
cfg.kf_max_dist = 5;
cfg.kf_mode = VPX_KF_AUTO;
cs->max_width = max_width;
cs->max_height = max_height;
rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
return -1;
}
rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
return -1;
}
return 0;
}
static int init_audio_encoder(CSSession *cs)
{
int rc = OPUS_OK;
cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate,
cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc);
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
return -1;
}
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate));
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
return -1;
}
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
return -1;
}
return 0;
}
static float calculate_sum_sq (int16_t *n, uint16_t k)
{
float result = 0;
uint16_t i = 0;
for ( ; i < k; i ++) result += (float) (n[i] * n[i]);
return result;
}
/* PUBLIC */
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
{
if (!cs || !length || length > cs->max_video_frame_size) {
LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
return -1;
}
cs->split_video_frame[0] = cs->frameid_out++;
cs->split_video_frame[1] = 0;
cs->processing_video_frame = payload;
cs->processing_video_frame_size = length;
return ((length - 1) / cs->video_frame_piece_size) + 1;
}
const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
{
if (!cs || !size) return NULL;
if (cs->processing_video_frame_size > cs->video_frame_piece_size) {
memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
cs->processing_video_frame,
cs->video_frame_piece_size);
cs->processing_video_frame += cs->video_frame_piece_size;
cs->processing_video_frame_size -= cs->video_frame_piece_size;
*size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE;
} else {
memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
cs->processing_video_frame,
cs->processing_video_frame_size);
*size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
}
cs->split_video_frame[1]++;
return cs->split_video_frame;
}
void cs_do(CSSession *cs)
{
if (!cs) return;
pthread_mutex_lock(cs->queue_mutex);
/*
if (!cs->active) {
pthread_mutex_unlock(cs->queue_mutex);
return;
}
/* Iterate over whole buffers and call playback callback * /
if (cs->abuf_ready) while (!DecodedAudioBuffer_empty(cs->abuf_ready)) {
DecodedAudio* p;
DecodedAudioBuffer_read(cs->abuf_ready, &p);
if (apcallback.first)
apcallback.first(cs->agent, cs->call_idx, p->data, p->size, apcallback.second);
free(p);
}
if (cs->vbuf_ready) while (!DecodedVideoBuffer_empty(cs->vbuf_ready)) {
vpx_image_t* p;
DecodedVideoBuffer_read(cs->vbuf_ready, &p);
if (vpcallback.first)
vpcallback.first(cs->agent, cs->call_idx, p, vpcallback.second);
vpx_img_free(p);
}
*/
Payload *p;
int rc;
if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) {
/* Decode audio */
buffer_read(cs->abuf_raw, &p);
uint16_t fsize = (cs->audio_decoder_channels *
(cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
int16_t tmp[fsize];
rc = opus_decode(cs->audio_decoder, p->data, p->size, tmp, fsize, (p->size == 0));
free(p);
if (rc < 0)
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
else
/* Play */
apcallback.first(cs->agent, cs->call_idx, tmp, rc, apcallback.second);
}
if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
/* Decode video */
buffer_read(cs->vbuf_raw, &p);
rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
free(p);
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
} else {
vpx_codec_iter_t iter = NULL;
vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter);
/* Play decoded images */
for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
vpcallback.first(cs->agent, cs->call_idx, dest, vpcallback.second);
vpx_img_free(dest);
}
}
}
pthread_mutex_unlock(cs->queue_mutex);
}
void cs_register_audio_callback(CSAudioCallback cb, void *data)
{
apcallback.first = cb;
apcallback.second = data;
}
void cs_register_video_callback(CSVideoCallback cb, void *data)
{
vpcallback.first = cb;
vpcallback.second = data;
}
int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height)
{ {
vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
@ -178,7 +484,7 @@ int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_
return 0; return 0;
} }
int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate) int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
{ {
vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
@ -197,122 +503,122 @@ int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate)
return 0; return 0;
} }
int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
{ {
vpx_codec_enc_cfg_t cfg; CSSession *cs = calloc(sizeof(CSSession), 1);
int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
if (rc != VPX_CODEC_OK) { if (!cs) {
LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); LOGGER_WARNING("Allocation failed! Application might misbehave!");
return -1;
}
cfg.rc_target_bitrate = video_bitrate;
cfg.g_w = max_width;
cfg.g_h = max_height;
cfg.g_pass = VPX_RC_ONE_PASS;
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
cfg.g_lag_in_frames = 0;
cfg.kf_min_dist = 0;
cfg.kf_max_dist = 5;
cfg.kf_mode = VPX_KF_AUTO;
cs->max_width = max_width;
cs->max_height = max_height;
cs->bitrate = video_bitrate;
rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
return -1;
}
rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
return -1;
}
return 0;
}
int init_audio_encoder(CodecState *cs, uint32_t audio_channels)
{
int rc = OPUS_OK;
cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc);
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
return -1;
}
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
return -1;
}
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
if ( rc != OPUS_OK ) {
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
return -1;
}
cs->audio_encoder_channels = audio_channels;
return 0;
}
CodecState *codec_init_session ( uint32_t audio_bitrate,
uint16_t audio_frame_duration,
uint32_t audio_sample_rate,
uint32_t encoder_audio_channels,
uint32_t decoder_audio_channels,
uint32_t audio_VAD_tolerance_ms,
uint16_t max_video_width,
uint16_t max_video_height,
uint32_t video_bitrate )
{
CodecState *retu = calloc(sizeof(CodecState), 1);
if (!retu) return NULL;
retu->audio_bitrate = audio_bitrate;
retu->audio_sample_rate = audio_sample_rate;
/* Encoders */
if (!max_video_width || !max_video_height) { /* Disable video */
/*video_width = 320;
video_height = 240; */
} else {
retu->capabilities |= ( 0 == init_video_encoder(retu, max_video_width, max_video_height,
video_bitrate) ) ? v_encoding : 0;
retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0;
}
retu->capabilities |= ( 0 == init_audio_encoder(retu, encoder_audio_channels) ) ? a_encoding : 0;
retu->capabilities |= ( 0 == init_audio_decoder(retu, decoder_audio_channels) ) ? a_decoding : 0;
if ( retu->capabilities == 0 ) { /* everything failed */
free (retu);
return NULL; return NULL;
} }
if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
LOGGER_WARNING("Jitter buffer creaton failed!");
goto error;
}
retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ? cs->audio_encoder_bitrate = cs_self->audio_bitrate;
audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration; cs->audio_encoder_sample_rate = cs_self->audio_sample_rate;
cs->audio_encoder_channels = cs_self->audio_channels;
cs->audio_encoder_frame_duration = cs_self->audio_frame_duration;
return retu; cs->audio_decoder_bitrate = cs_peer->audio_bitrate;
cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate;
cs->audio_decoder_channels = cs_peer->audio_channels;
cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? a_encoding : 0;
cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? a_decoding : 0;
if ( !(cs->capabilities & a_encoding) || !(cs->capabilities & a_decoding) ) goto error;
if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error;
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0) goto error;
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
if (pthread_mutexattr_destroy(&attr) != 0)
LOGGER_WARNING("Failed to destroy mutex attribute!");
goto error;
}
if (pthread_mutex_init(cs->queue_mutex, &attr) != 0) {
pthread_mutexattr_destroy(&attr);
goto error;
}
if ((cs->support_video = has_video)) {
cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
cs_self->max_video_height, cs_self->video_bitrate) ) ? v_encoding : 0;
cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? v_decoding : 0;
if ( !(cs->capabilities & v_encoding) || !(cs->capabilities & v_decoding) ) goto error;
if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) )
goto error;
if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
}
if (pthread_mutexattr_destroy(&attr) != 0)
LOGGER_WARNING("Failed to destroy mutex attribute!");
cs->active = 1;
return cs;
error:
LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
buffer_free(cs->abuf_raw);
if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder);
if (has_video) {
if ( cs->capabilities & v_decoding ) vpx_codec_destroy(&cs->v_decoder);
if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder);
buffer_free(cs->vbuf_raw);
free(cs->frame_buf);
free(cs->split_video_frame);
}
jbuf_free(cs->j_buf);
free(cs);
return NULL;
} }
void codec_terminate_session ( CodecState *cs ) void cs_kill(CSSession *cs)
{ {
if (!cs) return; if (!cs) return;
/* Lock running mutex and signal that cs is no longer active */
pthread_mutex_lock(cs->queue_mutex);
cs->active = 0;
/* Wait threads to close */
pthread_mutex_unlock(cs->queue_mutex);
pthread_mutex_lock(cs->queue_mutex);
pthread_mutex_unlock(cs->queue_mutex);
pthread_mutex_destroy(cs->queue_mutex);
if ( cs->audio_encoder ) if ( cs->audio_encoder )
opus_encoder_destroy(cs->audio_encoder); opus_encoder_destroy(cs->audio_encoder);
@ -325,21 +631,21 @@ void codec_terminate_session ( CodecState *cs )
if ( cs->capabilities & v_encoding ) if ( cs->capabilities & v_encoding )
vpx_codec_destroy(&cs->v_encoder); vpx_codec_destroy(&cs->v_encoder);
jbuf_free(cs->j_buf);
buffer_free(cs->abuf_raw);
buffer_free(cs->vbuf_raw);
free(cs->frame_buf);
LOGGER_DEBUG("Terminated codec state: %p", cs); LOGGER_DEBUG("Terminated codec state: %p", cs);
free(cs); free(cs);
} }
static float calculate_sum_sq (int16_t *n, uint16_t k) void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration)
{ {
float result = 0; cs->EVAD_tolerance = treshold > frame_duration ? treshold / frame_duration : frame_duration;
uint16_t i = 0;
for ( ; i < k; i ++) result += (float) (n[i] * n[i]);
return result;
} }
int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy)
{ {
float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size;
@ -355,3 +661,103 @@ int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy)
return 0; return 0;
} }
/* Called from RTP */
void queue_message(RTPSession *session, RTPMessage *msg)
{
CSSession *cs = session->cs;
if (!cs || !cs->active) return;
/* Audio */
if (session->payload_type == type_audio % 128) {
jbuf_write(cs->j_buf, msg);
pthread_mutex_lock(cs->queue_mutex);
int success = 0;
while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
Payload *p;
if (success == 2) {
p = malloc(sizeof(Payload));
if (p) p->size = 0;
} else {
p = malloc(sizeof(Payload) + msg->length);
if (p) {
p->size = msg->length;
memcpy(p->data, msg->data, msg->length);
}
rtp_free_msg(NULL, msg);
}
if (p) {
buffer_write(cs->abuf_raw, p);
} else {
LOGGER_WARNING("Allocation failed! Program might misbehave!");
}
}
pthread_mutex_unlock(cs->queue_mutex);
}
/* Video */
else {
uint8_t *packet = msg->data;
uint32_t packet_size = msg->length;
if (packet_size < VIDEOFRAME_HEADER_SIZE)
goto end;
if (packet[0] > cs->frameid_in || (msg->header->timestamp > cs->last_timestamp)) { /* New frame */
/* Flush last frames' data and get ready for this frame */
Payload *p = malloc(sizeof(Payload) + cs->frame_size);
if (p) {
pthread_mutex_lock(cs->queue_mutex);
if (buffer_full(cs->vbuf_raw)) {
LOGGER_DEBUG("Dropped video frame");
Payload *tp;
buffer_read(cs->vbuf_raw, &tp);
free(tp);
} else {
p->size = cs->frame_size;
memcpy(p->data, cs->frame_buf, cs->frame_size);
}
buffer_write(cs->vbuf_raw, p);
pthread_mutex_unlock(cs->queue_mutex);
} else {
LOGGER_WARNING("Allocation failed! Program might misbehave!");
goto end;
}
cs->last_timestamp = msg->header->timestamp;
cs->frameid_in = packet[0];
memset(cs->frame_buf, 0, cs->frame_size);
cs->frame_size = 0;
} else if (packet[0] < cs->frameid_in) { /* Old frame; drop */
LOGGER_DEBUG("Old packet: %u", packet[0]);
goto end;
}
/* Otherwise it's part of the frame so just process */
/* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
memcpy(cs->frame_buf + cs->frame_size,
packet + VIDEOFRAME_HEADER_SIZE,
packet_size - VIDEOFRAME_HEADER_SIZE);
cs->frame_size += packet_size - VIDEOFRAME_HEADER_SIZE;
end:
rtp_free_msg(NULL, msg);
}
}

View File

@ -24,6 +24,9 @@
#ifndef _CODEC_H_ #ifndef _CODEC_H_
#define _CODEC_H_ #define _CODEC_H_
#include "toxav.h"
#include "rtp.h"
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <pthread.h> #include <pthread.h>
@ -32,23 +35,30 @@
#include <vpx/vpx_encoder.h> #include <vpx/vpx_encoder.h>
#include <vpx/vp8dx.h> #include <vpx/vp8dx.h>
#include <vpx/vp8cx.h> #include <vpx/vp8cx.h>
#include <vpx/vpx_image.h>
#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) #define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) #define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
/* Audio encoding/decoding */ /* Audio encoding/decoding */
#include <opus.h> #include <opus.h>
typedef enum _Capabilities { typedef void (*CSAudioCallback) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
none, typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
typedef enum _CsCapabilities {
a_encoding = 1 << 0, a_encoding = 1 << 0,
a_decoding = 1 << 1, a_decoding = 1 << 1,
v_encoding = 1 << 2, v_encoding = 1 << 2,
v_decoding = 1 << 3 v_decoding = 1 << 3
} Capabilities; } CsCapabilities;
extern const uint16_t min_jbuf_size; typedef struct _CSSession {
typedef struct _CodecState { /* VIDEO
*
*
*/
int support_video;
/* video encoding */ /* video encoding */
vpx_codec_ctx_t v_encoder; vpx_codec_ctx_t v_encoder;
@ -56,61 +66,95 @@ typedef struct _CodecState {
/* video decoding */ /* video decoding */
vpx_codec_ctx_t v_decoder; vpx_codec_ctx_t v_decoder;
int bitrate;
int max_width; int max_width;
int max_height; int max_height;
/* 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 */
uint32_t last_timestamp; /* calculating cycles */
/* Limits */
uint32_t video_frame_piece_size;
uint32_t max_video_frame_size;
/* Reassembling */
uint8_t *split_video_frame;
const uint8_t *processing_video_frame;
uint16_t processing_video_frame_size;
/* AUDIO
*
*
*/
/* audio encoding */ /* audio encoding */
OpusEncoder *audio_encoder; OpusEncoder *audio_encoder;
int audio_bitrate; int audio_encoder_bitrate;
int audio_sample_rate; int audio_encoder_sample_rate;
int audio_encoder_frame_duration;
int audio_encoder_channels; int audio_encoder_channels;
/* audio decoding */ /* audio decoding */
OpusDecoder *audio_decoder; OpusDecoder *audio_decoder;
int audio_decoder_bitrate;
int audio_decoder_sample_rate;
int audio_decoder_frame_duration;
int audio_decoder_channels; int audio_decoder_channels;
uint64_t capabilities; /* supports*/ struct _JitterBuffer *j_buf;
/* Voice activity detection */ /* Voice activity detection */
uint32_t EVAD_tolerance; /* In frames */ uint32_t EVAD_tolerance; /* In frames */
uint32_t EVAD_tolerance_cr; uint32_t EVAD_tolerance_cr;
} CodecState;
typedef struct _JitterBuffer {
RTPMessage **queue;
uint32_t size;
uint32_t capacity;
uint16_t bottom;
uint16_t top;
} JitterBuffer;
JitterBuffer *create_queue(unsigned int capacity); /* OTHER
void terminate_queue(JitterBuffer *q); *
void queue(JitterBuffer *q, RTPMessage *pk); *
RTPMessage *dequeue(JitterBuffer *q, int *success); */
uint64_t capabilities; /* supports*/
CodecState *codec_init_session ( uint32_t audio_bitrate, /* Buffering */
uint16_t audio_frame_duration, void *abuf_raw, *vbuf_raw; /* Un-decoded data */
uint32_t audio_sample_rate, _Bool active;
uint32_t encoder_audio_channels, pthread_mutex_t queue_mutex[1];
uint32_t decoder_audio_channels,
uint32_t audio_VAD_tolerance_ms,
uint16_t max_video_width,
uint16_t max_video_height,
uint32_t video_bitrate );
void codec_terminate_session(CodecState *cs); void *agent; /* Pointer to ToxAv */
int32_t call_idx;
} CSSession;
CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video);
void cs_kill(CSSession *cs);
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length);
const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size);
/**
* Call playback callbacks
*/
void cs_do(CSSession *cs);
void cs_register_audio_callback(CSAudioCallback cb, void *data);
void cs_register_video_callback(CSVideoCallback cb, void *data);
/* Reconfigure video encoder; return 0 on success or -1 on failure. */
int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height);
int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate);
/* Reconfigure video encoder
return 0 on success.
return -1 on failure. */
int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height);
int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate);
/* Calculate energy and return 1 if has voice, 0 if not */ /* Calculate energy and return 1 if has voice, 0 if not */
int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy); int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy);
void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration);
/* Internal. Called from rtp_handle_message */
void queue_message(RTPSession *session, RTPMessage *msg);
#endif /* _CODEC_H_ */ #endif /* _CODEC_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -31,9 +31,8 @@ typedef uint8_t MSICallIDType[12];
typedef uint8_t MSIReasonStrType[255]; typedef uint8_t MSIReasonStrType[255];
typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg );
/** /**
* @brief Call type identifier. Also used as rtp callback prefix. * Call type identifier. Also used as rtp callback prefix.
*/ */
typedef enum { typedef enum {
type_audio = 192, type_audio = 192,
@ -42,7 +41,7 @@ typedef enum {
/** /**
* @brief Call state identifiers. * Call state identifiers.
*/ */
typedef enum { typedef enum {
call_inviting, /* when sending call invite */ call_inviting, /* when sending call invite */
@ -55,7 +54,7 @@ typedef enum {
/** /**
* @brief Encoding settings. * Encoding settings.
*/ */
typedef struct _MSICodecSettings { typedef struct _MSICodecSettings {
MSICallType call_type; MSICallType call_type;
@ -72,29 +71,24 @@ typedef struct _MSICodecSettings {
/** /**
* @brief Callbacks ids that handle the states * Callbacks ids that handle the states
*/ */
typedef enum { typedef enum {
/* Requests */ MSI_OnInvite, /* Incoming call */
MSI_OnInvite, MSI_OnRinging, /* When peer is ready to accept/reject the call */
MSI_OnStart, MSI_OnStart, /* Call (RTP transmission) started */
MSI_OnCancel, MSI_OnCancel, /* The side that initiated call canceled invite */
MSI_OnReject, MSI_OnReject, /* The side that was invited rejected the call */
MSI_OnEnd, MSI_OnEnd, /* Call that was active ended */
MSI_OnRequestTimeout, /* When the requested action didn't get response in specified time */
/* Responses */ MSI_OnPeerTimeout, /* Peer timed out; stop the call */
MSI_OnRinging, MSI_OnPeerCSChange, /* Peer requested Csettings change */
MSI_OnStarting, MSI_OnSelfCSChange /* Csettings change confirmation */
MSI_OnEnding,
/* Protocol */
MSI_OnRequestTimeout,
MSI_OnPeerTimeout,
MSI_OnMediaChange
} MSICallbackID; } MSICallbackID;
/** /**
* @brief Callbacks container * Callbacks container
*/ */
typedef struct _MSICallbackCont { typedef struct _MSICallbackCont {
MSICallbackType function; MSICallbackType function;
@ -102,16 +96,15 @@ typedef struct _MSICallbackCont {
} MSICallbackCont; } MSICallbackCont;
/** /**
* @brief The call struct. * The call struct.
*
*/ */
typedef struct _MSICall { /* Call info structure */ typedef struct _MSICall { /* Call info structure */
struct _MSISession *session; /* Session pointer */ struct _MSISession *session; /* Session pointer */
MSICallState state; MSICallState state;
MSICSettings csettings_local; /* Local call settings */ MSICSettings csettings_local; /* Local call settings */
MSICSettings *csettings_peer; /* Peers call settings */ MSICSettings *csettings_peer; /* Peers call settings */
MSICallIDType id; /* Random value identifying the call */ MSICallIDType id; /* Random value identifying the call */
@ -120,8 +113,6 @@ typedef struct _MSICall { /* Call info structure */
int request_timer_id; /* Timer id for outgoing request/action */ int request_timer_id; /* Timer id for outgoing request/action */
int ringing_timer_id; /* Timer id for ringing timeout */ int ringing_timer_id; /* Timer id for ringing timeout */
pthread_mutex_t mutex; /* */
uint32_t *peers; uint32_t *peers;
uint16_t peer_count; uint16_t peer_count;
@ -130,8 +121,7 @@ typedef struct _MSICall { /* Call info structure */
/** /**
* @brief Control session struct * Control session struct
*
*/ */
typedef struct _MSISession { typedef struct _MSISession {
@ -143,125 +133,71 @@ typedef struct _MSISession {
Messenger *messenger_handle; Messenger *messenger_handle;
uint32_t frequ; uint32_t frequ;
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
pthread_mutex_t mutex; pthread_mutex_t mutex;
void *timer_handler; void *timer_handler;
MSICallbackCont callbacks[11]; /* Callbacks used by this session */ MSICallbackCont callbacks[10]; /* Callbacks used by this session */
} MSISession; } MSISession;
/** /**
* @brief Callback setter. * Start the control session.
* */
* @param session The container. MSISession *msi_new ( Messenger *messenger, int32_t max_calls );
* @param callback The callback.
* @param id The id. /**
* @return void * Terminate control session.
*/
int msi_kill ( MSISession *session );
/**
* Callback setter.
*/ */
void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata);
/** /**
* @brief Start the control session. * Send invite request to friend_id.
*
* @param messenger Tox* object.
* @param max_calls Amount of calls possible
* @return MSISession* The created session.
* @retval NULL Error occurred.
*/ */
MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ); int msi_invite ( MSISession *session,
int32_t *call_index,
const MSICSettings *csettings,
/** uint32_t rngsec,
* @brief Terminate control session.
*
* @param session The session
* @return int
*/
int msi_terminate_session ( MSISession *session );
/**
* @brief Send invite request to friend_id.
*
* @param session Control session.
* @param call_index Set to new call index.
* @param call_type Type of the call. Audio or Video(both audio and video)
* @param rngsec Ringing timeout.
* @param friend_id The friend.
* @return int
*/
int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec,
uint32_t friend_id ); uint32_t friend_id );
/** /**
* @brief Hangup active call. * Hangup active call.
*
* @param session Control session.
* @param call_index To which call is this action handled.
* @return int
* @retval -1 Error occurred.
* @retval 0 Success.
*/ */
int msi_hangup ( MSISession *session, int32_t call_index ); int msi_hangup ( MSISession *session, int32_t call_index );
/** /**
* @brief Answer active call request. * Answer active call request.
*
* @param session Control session.
* @param call_index To which call is this action handled.
* @param call_type Answer with Audio or Video(both).
* @return int
*/ */
int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings ); int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
/** /**
* @brief Cancel request. * Cancel request.
*
* @param session Control session.
* @param call_index To which call is this action handled.
* @param peer To which peer.
* @param reason Set optional reason header. Pass NULL if none.
* @return int
*/ */
int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ); int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason );
/** /**
* @brief Reject request. * Reject incoming call.
*
* @param session Control session.
* @param call_index To which call is this action handled.
* @param reason Set optional reason header. Pass NULL if none.
* @return int
*/ */
int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); int msi_reject ( MSISession *session, int32_t call_index, const char *reason );
/** /**
* @brief Send invite request to friend_id. * Terminate the call.
*
* @param session Control session.
* @param call_index Call index.
* @param call_type Type of the call. Audio or Video(both audio and video)
* @param rngsec Ringing timeout.
* @param friend_id The friend.
* @return int
*/
int msi_change_csettings ( MSISession *session, int32_t call_index, MSICSettings csettings );
/**
* @brief Terminate the current call.
*
* @param session Control session.
* @param call_index To which call is this action handled.
* @return int
*/ */
int msi_stopcall ( MSISession *session, int32_t call_index ); int msi_stopcall ( MSISession *session, int32_t call_index );
/**
* Change codec settings of the current call.
*/
int msi_change_csettings ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
/**
* Main msi loop
*/
void msi_do( MSISession *session );
#endif /* __TOXMSI */ #endif /* __TOXMSI */

View File

@ -28,7 +28,7 @@
#include "rtp.h" #include "rtp.h"
#include <stdlib.h> #include <stdlib.h>
void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg); void queue_message(RTPSession *_session, RTPMessage *_msg);
#define size_32 4 #define size_32 4
@ -47,15 +47,9 @@ void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg);
#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
/** /**
* @brief Checks if message came in late. * Checks if message came in late.
*
* @param session Control session.
* @param msg The message.
* @return int
* @retval -1 The message came in order.
* @retval 0 The message came late.
*/ */
inline__ int check_late_message (RTPSession *session, RTPMessage *msg) static int check_late_message (RTPSession *session, RTPMessage *msg)
{ {
/* /*
* Check Sequence number. If this new msg has lesser number then the session->rsequnum * Check Sequence number. If this new msg has lesser number then the session->rsequnum
@ -67,12 +61,7 @@ inline__ int check_late_message (RTPSession *session, RTPMessage *msg)
/** /**
* @brief Extracts header from payload. * Extracts header from payload.
*
* @param payload The payload.
* @param length The size of payload.
* @return RTPHeader* Extracted header.
* @retval NULL Error occurred while extracting header.
*/ */
RTPHeader *extract_header ( const uint8_t *payload, int length ) RTPHeader *extract_header ( const uint8_t *payload, int length )
{ {
@ -147,12 +136,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
} }
/** /**
* @brief Extracts external header from payload. Must be called AFTER extract_header()! * Extracts external header from payload. Must be called AFTER extract_header()!
*
* @param payload The ITERATED payload.
* @param length The size of payload.
* @return RTPExtHeader* Extracted extension header.
* @retval NULL Error occurred while extracting extension header.
*/ */
RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
{ {
@ -200,11 +184,7 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
} }
/** /**
* @brief Adds header to payload. Make sure _payload_ has enough space. * Adds header to payload. Make sure _payload_ has enough space.
*
* @param header The header.
* @param payload The payload.
* @return uint8_t* Iterated position.
*/ */
uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
{ {
@ -245,11 +225,7 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
} }
/** /**
* @brief Adds extension header to payload. Make sure _payload_ has enough space. * Adds extension header to payload. Make sure _payload_ has enough space.
*
* @param header The header.
* @param payload The payload.
* @return uint8_t* Iterated position.
*/ */
uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
{ {
@ -279,10 +255,7 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
} }
/** /**
* @brief Builds header from control session values. * Builds header from control session values.
*
* @param session Control session.
* @return RTPHeader* Created header.
*/ */
RTPHeader *build_header ( RTPSession *session ) RTPHeader *build_header ( RTPSession *session )
{ {
@ -316,16 +289,8 @@ RTPHeader *build_header ( RTPSession *session )
/** /**
* @brief Parses data into RTPMessage struct. Stores headers separately from the payload data * Parses data into RTPMessage struct. Stores headers separately from the payload data
* and so the length variable is set accordingly. _sequnum_ argument is * and so the length variable is set accordingly.
* passed by the handle_packet() since it's parsed already.
*
* @param session Control session.
* @param sequnum Sequence number that's parsed from payload in handle_packet()
* @param data Payload data.
* @param length Payload size.
* @return RTPMessage*
* @retval NULL Error occurred.
*/ */
RTPMessage *msg_parse ( const uint8_t *data, int length ) RTPMessage *msg_parse ( const uint8_t *data, int length )
{ {
@ -373,15 +338,7 @@ RTPMessage *msg_parse ( const uint8_t *data, int length )
} }
/** /**
* @brief Callback for networking core. * Callback for networking core.
*
* @param object RTPSession object.
* @param ip_port Where the message comes from.
* @param data Message data.
* @param length Message length.
* @return int
* @retval -1 Error occurred.
* @retval 0 Success.
*/ */
int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length )
{ {
@ -406,22 +363,13 @@ int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length )
_session->timestamp = _msg->header->timestamp; _session->timestamp = _msg->header->timestamp;
} }
toxav_handle_packet(_session, _msg); queue_message(_session, _msg);
return 0; return 0;
} }
/** /**
* @brief Stores headers and payload data in one container ( data ) * Allocate message and store data there
* and the length is set accordingly. Returned message is used for sending _only_.
*
* @param session The control session.
* @param data Payload data to send ( This is what you pass ).
* @param length Size of the payload data.
* @return RTPMessage* Created message.
* @retval NULL Error occurred.
*/ */
RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length ) RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
{ {
@ -472,28 +420,15 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t
} }
/**
* @brief Sends data to _RTPSession::dest
*
* @param session The session.
* @param messenger Tox* object.
* @param data The payload.
* @param length Size of the payload.
* @return int
* @retval -1 On error.
* @retval 0 On success.
*/
int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length )
{ {
RTPMessage *msg = rtp_new_message (session, data, length); RTPMessage *msg = rtp_new_message (session, data, length);
if ( !msg ) { if ( !msg ) return -1;
LOGGER_WARNING("No session!");
return -1;
}
if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) {
LOGGER_WARNING("Failed to send full packet! std error: %s", strerror(errno)); LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
rtp_free_msg ( session, msg ); rtp_free_msg ( session, msg );
return -1; return -1;
} }
@ -506,15 +441,6 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat
return 0; return 0;
} }
/**
* @brief Speaks for it self.
*
* @param session The control session msg belongs to. You set it as NULL when freeing recved messages.
* Otherwise set it to session the message was created from.
* @param msg The message.
* @return void
*/
void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
{ {
if ( !session ) { if ( !session ) {
@ -533,17 +459,7 @@ void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
free ( msg ); free ( msg );
} }
/** RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
* @brief Must be called before calling any other rtp function. It's used
* to initialize RTP control session.
*
* @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
* @param messenger Tox* object.
* @param friend_num Friend id.
* @return RTPSession* Created control session.
* @retval NULL Error occurred.
*/
RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num )
{ {
RTPSession *_retu = calloc(1, sizeof(RTPSession)); RTPSession *_retu = calloc(1, sizeof(RTPSession));
@ -593,17 +509,7 @@ RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int frien
return _retu; return _retu;
} }
void rtp_kill ( RTPSession *session, Messenger *messenger )
/**
* @brief Terminate the session.
*
* @param session The session.
* @param messenger The messenger who owns the session
* @return int
* @retval -1 Error occurred.
* @retval 0 Success.
*/
void rtp_terminate_session ( RTPSession *session, Messenger *messenger )
{ {
if ( !session ) return; if ( !session ) return;

View File

@ -24,19 +24,15 @@
#define RTP_VERSION 2 #define RTP_VERSION 2
#include <inttypes.h> #include <inttypes.h>
#include <pthread.h> // #include <pthread.h>
#include "../toxcore/util.h"
#include "../toxcore/network.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/Messenger.h" #include "../toxcore/Messenger.h"
#define MAX_SEQU_NUM 65535 #define MAX_SEQU_NUM 65535
#define MAX_RTP_SIZE 65535 #define MAX_RTP_SIZE 65535
/** /**
* @brief Standard rtp header * Standard rtp header
*
*/ */
typedef struct _RTPHeader { typedef struct _RTPHeader {
@ -52,8 +48,7 @@ typedef struct _RTPHeader {
/** /**
* @brief Standard rtp extension header. * Standard rtp extension header.
*
*/ */
typedef struct _RTPExtHeader { typedef struct _RTPExtHeader {
uint16_t type; /* Extension profile */ uint16_t type; /* Extension profile */
@ -64,8 +59,7 @@ typedef struct _RTPExtHeader {
/** /**
* @brief Standard rtp message. * Standard rtp message.
*
*/ */
typedef struct _RTPMessage { typedef struct _RTPMessage {
RTPHeader *header; RTPHeader *header;
@ -79,11 +73,11 @@ typedef struct _RTPMessage {
/** /**
* @brief Our main session descriptor. * Our main session descriptor.
* It measures the session variables and controls * It measures the session variables and controls
* the entire session. There are functions for manipulating * the entire session. There are functions for manipulating
* the session so tend to use those instead of directly modifying * the session so tend to use those instead of directly modifying
* session parameters. * session parameters.
* *
*/ */
typedef struct _RTPSession { typedef struct _RTPSession {
@ -109,88 +103,31 @@ typedef struct _RTPSession {
uint8_t prefix; uint8_t prefix;
int dest; int dest;
int32_t call_index;
struct _ToxAv *av; struct _CSSession *cs;
} RTPSession; } RTPSession;
/** /**
* @brief Release all messages held by session. * Must be called before calling any other rtp function.
*
* @param session The session.
* @return int
* @retval -1 Error occurred.
* @retval 0 Success.
*/ */
int rtp_release_session_recv ( RTPSession *session ); RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num );
/** /**
* @brief Call this to change queue limit * Terminate the session.
*
* @param session The session
* @param limit new limit
* @return void
*/ */
void rtp_queue_adjust_limit ( RTPSession *session, uint64_t limit ); void rtp_kill ( RTPSession *session, Messenger *messenger );
/** /**
* @brief Get's oldest message in the list. * Sends msg to _RTPSession::dest
*
* @param session Where the list is.
* @return RTPMessage* The message. You need to call rtp_msg_free() to free it.
* @retval NULL No messages in the list, or no list.
*/
RTPMessage *rtp_recv_msg ( RTPSession *session );
/**
* @brief Sends msg to _RTPSession::dest
*
* @param session The session.
* @param msg The message
* @param messenger Tox* object.
* @return int
* @retval -1 On error.
* @retval 0 On success.
*/ */
int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
/** /**
* @brief Speaks for it self. * Dealloc msg.
*
* @param session The control session msg belongs to. It can be NULL.
* @param msg The message.
* @return void
*/ */
void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
/**
* @brief Must be called before calling any other rtp function. It's used
* to initialize RTP control session.
*
* @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
* @param messenger Tox* object.
* @param friend_num Friend id.
* @return RTPSession* Created control session.
* @retval NULL Error occurred.
*/
RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num );
/**
* @brief Terminate the session.
*
* @param session The session.
* @param messenger The messenger who owns the session
* @return int
* @retval -1 Error occurred.
* @retval 0 Success.
*/
void rtp_terminate_session ( RTPSession *session, Messenger *messenger );
#endif /* __TOXRTP */ #endif /* __TOXRTP */

File diff suppressed because it is too large Load Diff

View File

@ -28,11 +28,11 @@
extern "C" { extern "C" {
#endif #endif
/* vpx_image_t */
#include <vpx/vpx_image.h>
typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
typedef struct _ToxAv ToxAv; typedef struct _ToxAv ToxAv;
typedef struct vpx_image vpx_image_t;
typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
#ifndef __TOX_DEFINED__ #ifndef __TOX_DEFINED__
#define __TOX_DEFINED__ #define __TOX_DEFINED__
@ -43,34 +43,28 @@ typedef struct Tox Tox;
/** /**
* @brief Callbacks ids that handle the call states. * Callbacks ids that handle the call states.
*/ */
typedef enum { typedef enum {
/* Requests */ av_OnInvite, /* Incoming call */
av_OnInvite, av_OnRinging, /* When peer is ready to accept/reject the call */
av_OnStart, av_OnStart, /* Call (RTP transmission) started */
av_OnCancel, av_OnCancel, /* The side that initiated call canceled invite */
av_OnReject, av_OnReject, /* The side that was invited rejected the call */
av_OnEnd, av_OnEnd, /* Call that was active ended */
av_OnRequestTimeout, /* When the requested action didn't get response in specified time */
/* Responses */ av_OnPeerTimeout, /* Peer timed out; stop the call */
av_OnRinging, av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */
av_OnStarting, av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */
av_OnEnding,
/* Protocol */
av_OnRequestTimeout,
av_OnPeerTimeout,
av_OnMediaChange
} ToxAvCallbackID; } ToxAvCallbackID;
/** /**
* @brief Call type identifier. * Call type identifier.
*/ */
typedef enum { typedef enum {
TypeAudio = 192, av_TypeAudio = 192,
TypeVideo av_TypeVideo
} ToxAvCallType; } ToxAvCallType;
@ -84,41 +78,35 @@ typedef enum {
} ToxAvCallState; } ToxAvCallState;
/** /**
* @brief Error indicators. * Error indicators.
*/ */
typedef enum { typedef enum {
ErrorNone = 0, av_ErrorNone = 0,
ErrorInternal = -1, /* Internal error */ av_ErrorInternal = -1, /* Internal error */
ErrorAlreadyInCall = -2, /* Already has an active call */ av_ErrorAlreadyInCall = -2, /* Already has an active call */
ErrorNoCall = -3, /* Trying to perform call action while not in a call */ av_ErrorNoCall = -3, /* Trying to perform call action while not in a call */
ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ av_ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ av_ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
ErrorAudioPacketLost = -6, /* Indicating packet loss */ av_ErrorInvalidCodecState = -6, /* Codec state not initialized */
ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */ av_ErrorPacketTooLarge = -7, /* Split packet exceeds it's limit */
ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
ErrorTerminatingAudioRtp = -9, /* Returned in toxav_kill_transmission() */
ErrorTerminatingVideoRtp = -10, /* Returned in toxav_kill_transmission() */
ErrorPacketTooLarge = -11, /* Buffer exceeds size while encoding */
ErrorInvalidCodecState = -12, /* Codec state not initialized */
} ToxAvError; } ToxAvError;
/** /**
* @brief Locally supported capabilities. * Locally supported capabilities.
*/ */
typedef enum { typedef enum {
AudioEncoding = 1 << 0, av_AudioEncoding = 1 << 0,
AudioDecoding = 1 << 1, av_AudioDecoding = 1 << 1,
VideoEncoding = 1 << 2, av_VideoEncoding = 1 << 2,
VideoDecoding = 1 << 3 av_VideoDecoding = 1 << 3
} ToxAvCapabilities; } ToxAvCapabilities;
/** /**
* @brief Encoding settings. * Encoding settings.
*/ */
typedef struct _ToxAvCodecSettings { typedef struct _ToxAvCSettings {
ToxAvCallType call_type; ToxAvCallType call_type;
uint32_t video_bitrate; /* In kbits/s */ uint32_t video_bitrate; /* In kbits/s */
@ -132,256 +120,165 @@ typedef struct _ToxAvCodecSettings {
} ToxAvCSettings; } ToxAvCSettings;
extern const ToxAvCSettings av_DefaultSettings; extern const ToxAvCSettings av_DefaultSettings;
extern const uint32_t av_jbufdc; /* Jitter buffer default capacity */
extern const uint32_t av_VADd; /* VAD default treshold */
/** /**
* @brief Start new A/V session. There can only be one session at the time. If you register more * Start new A/V session. There can only be one session at the time.
* it will result in undefined behaviour.
*
* @param messenger The messenger handle.
* @param userdata The agent handling A/V session (i.e. phone).
* @param video_width Width of video frame.
* @param video_height Height of video frame.
* @return ToxAv*
* @retval NULL On error.
*/ */
ToxAv *toxav_new(Tox *messenger, int32_t max_calls); ToxAv *toxav_new(Tox *messenger, int32_t max_calls);
/** /**
* @brief Remove A/V session. * Remove A/V session.
*
* @param av Handler.
* @return void
*/ */
void toxav_kill(ToxAv *av); void toxav_kill(ToxAv *av);
/** /**
* @brief Register callback for call state. * Returns the interval in milliseconds when the next toxav_do() should be called.
* * If no call is active at the moment returns 200.
* @param av Handler.
* @param callback The callback
* @param id One of the ToxAvCallbackID values
* @return void
*/ */
void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata); uint32_t toxav_do_interval(ToxAv *av);
/** /**
* @brief Register callback for receiving audio data * Main loop for the session. Best called right after tox_do();
*
* @param av Handler.
* @param callback The callback
* @return void
*/ */
void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *), void toxav_do(ToxAv *av);
void *user_data);
/** /**
* @brief Register callback for receiving video data * Register callback for call state.
*
* @param av Handler.
* @param callback The callback
* @return void
*/ */
void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *), void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata);
void *user_data);
/** /**
* @brief Call user. Use its friend_id. * Register callback for audio data.
*
* @param av Handler.
* @param user The user.
* @param call_type Call type.
* @param ringing_seconds Ringing timeout.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_call(ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds); void toxav_register_audio_callback (ToxAvAudioCallback cb, void *userdata);
/** /**
* @brief Hangup active call. * Register callback for video data.
* */
* @param av Handler. void toxav_register_video_callback (ToxAvVideoCallback cb, void *userdata);
* @return int
* @retval 0 Success. /**
* @retval ToxAvError On error. * Call user. Use its friend_id.
*/
int toxav_call(ToxAv *av,
int32_t *call_index,
int friend_id,
const ToxAvCSettings *csettings,
int ringing_seconds);
/**
* Hangup active call.
*/ */
int toxav_hangup(ToxAv *av, int32_t call_index); int toxav_hangup(ToxAv *av, int32_t call_index);
/** /**
* @brief Answer incomming call. * Answer incoming call. Pass the csettings that you will use.
*
* @param av Handler.
* @param call_type Answer with...
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings );
/** /**
* @brief Reject incomming call. * Reject incoming call.
*
* @param av Handler.
* @param reason Optional reason. Set NULL if none.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); int toxav_reject(ToxAv *av, int32_t call_index, const char *reason);
/** /**
* @brief Cancel outgoing request. * Cancel outgoing request.
*
* @param av Handler.
* @param reason Optional reason.
* @param peer_id peer friend_id
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason);
/** /**
* @brief Notify peer that we are changing call settings * Notify peer that we are changing codec settings.
*
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings);
/** /**
* @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. * Terminate transmission. Note that transmission will be
* * terminated without informing remote peer. Usually called when we can't inform peer.
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_stop_call(ToxAv *av, int32_t call_index); int toxav_stop_call(ToxAv *av, int32_t call_index);
/** /**
* @brief Must be call before any RTP transmission occurs. * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*.
* * Also, it must be called when call is started
* @param av Handler.
* @param support_video Is video supported ? 1 : 0
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_prepare_transmission(ToxAv *av, int32_t call_index, uint32_t jbuf_size, uint32_t VAD_treshold, int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video);
int support_video);
/** /**
* @brief Call this at the end of the transmission. * Clears transmission data. Call this at the end of the transmission.
*
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_kill_transmission(ToxAv *av, int32_t call_index); int toxav_kill_transmission(ToxAv *av, int32_t call_index);
/** /**
* @brief Encode and send video packet. * Encode video frame.
*
* @param av Handler.
* @param frame The encoded frame.
* @param frame_size The size of the encoded frame.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/ */
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size); int toxav_prepare_video_frame ( ToxAv *av,
int32_t call_index,
uint8_t *dest,
int dest_max,
vpx_image_t *input);
/** /**
* @brief Send audio frame. * Send encoded video packet.
* */
* @param av Handler. int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size);
* @param data The audio data encoded with toxav_prepare_audio_frame().
* @param size Its size in number of bytes. /**
* @return int * Encode audio frame.
* @retval 0 Success. */
* @retval ToxAvError On error. int toxav_prepare_audio_frame ( ToxAv *av,
int32_t call_index,
uint8_t *dest,
int dest_max,
const int16_t *frame,
int frame_size);
/**
* Send encoded audio frame.
*/ */
int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size);
/** /**
* @brief Encode video frame * Get codec settings from the peer. These were exchanged during call initialization
* * or when peer send us new csettings.
* @param av Handler
* @param dest Where to
* @param dest_max Max size
* @param input What to encode
* @return int
* @retval ToxAvError On error.
* @retval >0 On success
*/
int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input );
/**
* @brief Encode audio frame
*
* @param av Handler
* @param dest dest
* @param dest_max Max dest size
* @param frame The frame
* @param frame_size The frame size
* @return int
* @retval ToxAvError On error.
* @retval >0 On success
*/
int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame,
int frame_size);
/**
* @brief Get peer transmission type. It can either be audio or video.
*
* @param av Handler.
* @param peer The peer
* @return int
* @retval ToxAvCallType On success.
* @retval ToxAvError On error.
*/ */
int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest );
/** /**
* @brief Get id of peer participating in conversation * Get friend id of peer participating in conversation.
*
* @param av Handler
* @param peer peer index
* @return int
* @retval ToxAvError No peer id
*/ */
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer );
/** /**
* @brief Get current call state * Get current call state.
*
* @param av Handler
* @param call_index What call
* @return int
* @retval ToxAvCallState State id
*/ */
ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index );
/** /**
* @brief Is certain capability supported * Is certain capability supported. Used to determine if encoding/decoding is ready.
*
* @param av Handler
* @return int
* @retval 1 Yes.
* @retval 0 No.
*/ */
int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability );
/**
* Returns tox reference.
*/
Tox *toxav_get_tox (ToxAv *av);
Tox *toxav_get_tox(ToxAv *av); /**
* Set VAD activity treshold for calculating VAD. 40 is some middle value for treshold
*/
int toxav_set_vad_treshold (ToxAv *av, int32_t call_index, uint32_t treshold);
int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy ); /**
* Check if there is activity in the PCM data.
* Activity is present if the calculated PCM energy is > ref_energy.
* Returns bool.
*/
int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref);
/**
* Returns number of active calls or -1 on error.
*/
int toxav_get_active_count (ToxAv *av);
/* Create a new toxav group. /* Create a new toxav group.
* *

View File

@ -36,9 +36,6 @@
#include "util.h" #include "util.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status); static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status);
static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
uint32_t length, uint8_t congestion_control); uint32_t length, uint8_t congestion_control);

View File

@ -80,7 +80,7 @@ const char *logger_stringify_level(LoggerLevel level)
static const char *strings [] = { static const char *strings [] = {
"INFO", "INFO",
"DEBUG", "DEBUG",
"WARNING", "WARN",
"ERROR" "ERROR"
}; };
@ -143,11 +143,26 @@ void logger_write (LoggerLevel level, const char *format, ...)
char *logger_timestr(char *dest, size_t max_size) char *logger_timestr(char *dest, size_t max_size)
{ {
uint64_t diff = (current_time_monotonic() - logger.start_time); /* ms */ time_t timer;
struct tm *tm_info;
time(&timer);
tm_info = localtime(&timer);
strftime(dest, max_size, "%m:%d %H:%M:%S", tm_info);
return dest;
/*uint64_t diff = (current_time_monotonic() - logger.start_time); /* ms * /
snprintf(dest, max_size, "%"PRIu64"", diff); snprintf(dest, max_size, "%"PRIu64"", diff);
return dest; */
}
char *logger_posstr (char *dest, size_t max_size, const char *file, int line)
{
snprintf(dest, max_size, "%s:%d", file, line);
return dest; return dest;
} }
#endif /* LOGGING */ #endif /* LOGGING */

View File

@ -44,6 +44,7 @@ const char *logger_stringify_level(LoggerLevel level);
unsigned logger_get_pid(); unsigned logger_get_pid();
void logger_write (LoggerLevel level, const char *format, ...); void logger_write (LoggerLevel level, const char *format, ...);
char *logger_timestr (char *dest, size_t max_size); char *logger_timestr (char *dest, size_t max_size);
char *logger_posstr (char *dest, size_t max_size, const char *file, int line);
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define _SFILE (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) #define _SFILE (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
@ -51,16 +52,19 @@ char *logger_timestr (char *dest, size_t max_size);
#define _SFILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define _SFILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif #endif
#define WRITE_FORMAT(__LEVEL__, format) char __time__[20]; char* the_str = calloc(sizeof(char), strlen(format)+ 500); sprintf(the_str, "\n[%u] [%s] [%s] [%s:%d %s()] %s", \ #define LFORMAT "\n%-15s %-7u %-5s %-20s - %s"
logger_get_pid(), logger_stringify_level(__LEVEL__), logger_timestr(__time__, 20), _SFILE, __LINE__, __func__, format) #define WRITE_FORMAT(__LEVEL__, __WHAT__) \
char __time__[15]; char posstr[200]; char the_str [4096]; \
snprintf(the_str, 4096, LFORMAT, logger_timestr(__time__, 15), logger_get_pid(), \
logger_stringify_level(__LEVEL__), logger_posstr(posstr, 200, _SFILE, __LINE__), __WHAT__)
/* Use these macros */ /* Use these macros */
#define LOGGER_INIT(name, level) logger_init(name, level); #define LOGGER_INIT(name, level) logger_init(name, level);
#define LOGGER_INFO(format, ...) do { WRITE_FORMAT(INFO, format); logger_write( INFO, the_str, ##__VA_ARGS__ ); free(the_str); } while (0) #define LOGGER_INFO(format, ...) do { WRITE_FORMAT(INFO, format); logger_write( INFO, the_str, ##__VA_ARGS__ ); } while (0)
#define LOGGER_DEBUG(format, ...) do { WRITE_FORMAT(DEBUG, format); logger_write( DEBUG, the_str, ##__VA_ARGS__ ); free(the_str); } while (0) #define LOGGER_DEBUG(format, ...) do { WRITE_FORMAT(DEBUG, format); logger_write( DEBUG, the_str, ##__VA_ARGS__ ); } while (0)
#define LOGGER_WARNING(format, ...) do { WRITE_FORMAT(WARNING, format); logger_write( WARNING, the_str, ##__VA_ARGS__ ); free(the_str); } while (0) #define LOGGER_WARNING(format, ...) do { WRITE_FORMAT(WARNING, format); logger_write( WARNING, the_str, ##__VA_ARGS__ ); } while (0)
#define LOGGER_ERROR(format, ...) do { WRITE_FORMAT(ERROR, format); logger_write( ERROR, the_str, ##__VA_ARGS__ ); free(the_str); } while (0) #define LOGGER_ERROR(format, ...) do { WRITE_FORMAT(ERROR, format); logger_write( ERROR, the_str, ##__VA_ARGS__ ); } while (0)
/* To do some checks or similar only when logging use this */ /* To do some checks or similar only when logging use this */
#define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0) #define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)

View File

@ -28,7 +28,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#define inline__ inline __attribute__((always_inline)) #define MIN(a,b) (((a)<(b))?(a):(b))
void unix_time_update(); void unix_time_update();
uint64_t unix_time(); uint64_t unix_time();