From 1aeeef58b2b9e817563c9655d6b736cf41d1c110 Mon Sep 17 00:00:00 2001 From: mannol Date: Mon, 21 Jul 2014 01:10:57 +0200 Subject: [PATCH] Improved protocol and cleaned code a bit --- .gitignore | 4 + auto_tests/toxav_basic_test.c | 273 ++++---- auto_tests/toxav_many_test.c | 146 +++-- toxav/codec.c | 34 +- toxav/codec.h | 3 + toxav/msi.c | 1147 ++++++++++++++------------------- toxav/msi.h | 103 +-- toxav/rtp.c | 141 +--- toxav/rtp.h | 12 +- toxav/toxav.c | 486 ++++++-------- toxav/toxav.h | 68 +- 11 files changed, 1041 insertions(+), 1376 deletions(-) diff --git a/.gitignore b/.gitignore index 0b86e839..debe5762 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,10 @@ libtool .deps .libs .dirstamp +build/ + +#kdevelop +*.kdev* # Netbeans nbproject diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index a1bccd7c..d3f21fc1 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -31,7 +31,8 @@ typedef enum _CallStatus { Ringing, Ended, Rejected, - Cancel + Cancel, + TimedOut } CallStatus; @@ -59,7 +60,7 @@ void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *dat /******************************************************************************/ -void callback_recv_invite ( int32_t call_index, void *_arg ) +void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; @@ -67,23 +68,23 @@ void callback_recv_invite ( int32_t call_index, void *_arg ) cast->Bob.status = Ringing; cast->Bob.call_index = call_index; } -void callback_recv_ringing ( int32_t call_index, void *_arg ) +void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; /* Alice always sends invite */ cast->Alice.status = Ringing; } -void callback_recv_starting ( int32_t call_index, void *_arg ) +void callback_recv_starting ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; /* Alice always sends invite */ printf("Call started on Alice side...\n"); cast->Alice.status = InCall; - toxav_prepare_transmission(cast->Alice.av, call_index, &muhcaps, 1); + toxav_prepare_transmission(av, call_index, &muhcaps, 1); } -void callback_recv_ending ( int32_t call_index, void *_arg ) +void callback_recv_ending ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; @@ -96,28 +97,24 @@ void callback_recv_ending ( int32_t call_index, void *_arg ) } } -void callback_recv_error ( int32_t call_index, void *_arg ) -{ - ck_assert_msg(0, "AV internal error"); -} -void callback_call_started ( int32_t call_index, void *_arg ) +void callback_call_started ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; /* Alice always sends invite */ printf("Call started on Bob side...\n"); cast->Bob.status = InCall; - toxav_prepare_transmission(cast->Bob.av, call_index, &muhcaps, 1); + toxav_prepare_transmission(av, call_index, &muhcaps, 1); } -void callback_call_canceled ( int32_t call_index, void *_arg ) +void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; printf ( "Call Canceled for Bob!\n" ); cast->Bob.status = Cancel; } -void callback_call_rejected ( int32_t call_index, void *_arg ) +void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; @@ -126,22 +123,58 @@ void callback_call_rejected ( int32_t call_index, void *_arg ) /* If Bob rejects, call is ended for alice and she sends ending */ cast->Alice.status = Rejected; } -void callback_call_ended ( int32_t call_index, void *_arg ) +void callback_call_ended ( void *av, int32_t call_index, void *_arg ) { Status *cast = _arg; - + printf ( "Call ended for Bob!\n" ); cast->Bob.status = Ended; } -void callback_requ_timeout ( int32_t call_index, void *_arg ) +void callback_call_type_change ( void *av, int32_t call_index, void *_arg ) { - ck_assert_msg(0, "No answer!"); + printf("Call type changed; new type: %s!\n", toxav_get_peer_transmission_type + (av, call_index, 0) == TypeAudio ? "audio" : "video"); +} + +void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) +{ + Status *cast = _arg; + printf("Call timed-out!"); + cast->Alice.status = TimedOut; +} + +static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length) +{ +} + +static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img) +{ +} + +void register_callbacks(ToxAv* av, void* data) +{ + toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); + toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, 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_recv_invite, av_OnInvite, 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_call_type_change, av_OnMediaChange, data); + + + toxav_register_audio_recv_callback(av, callback_audio); + toxav_register_video_recv_callback(av, callback_video); } /*************************************************************************************************/ /* Alice calls bob and the call starts. - * What happens in the call is defined after. To quit the loop use: step++; + * What happens during the call is defined after. To quit the loop use: step++; */ #define CALL_AND_START_LOOP(AliceCallType, BobCallType) \ { int step = 0, running = 1; while (running) {\ @@ -199,7 +232,7 @@ START_TEST(test_AV_flows) printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); muhcaps = av_DefaultSettings; - muhcaps.video_height = muhcaps.video_width = 128; + muhcaps.max_video_height = muhcaps.max_video_width = 128; Status status_control = { {none, toxav_new(Alice, 1), NULL, -1}, @@ -209,19 +242,8 @@ START_TEST(test_AV_flows) ck_assert_msg(status_control.Alice.av || status_control.Bob.av, "Failed to create 2 toxav instances"); - toxav_register_callstate_callback(callback_call_started, av_OnStart, &status_control); - toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, &status_control); - toxav_register_callstate_callback(callback_call_rejected, av_OnReject, &status_control); - toxav_register_callstate_callback(callback_call_ended, av_OnEnd, &status_control); - toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, &status_control); - - toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, &status_control); - toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, &status_control); - toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, &status_control); - - toxav_register_callstate_callback(callback_recv_error, av_OnError, &status_control); - toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, &status_control); - + register_callbacks(status_control.Alice.av, &status_control); + register_callbacks(status_control.Bob.av, &status_control); const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); int16_t sample_payload[frame_size]; @@ -266,23 +288,6 @@ START_TEST(test_AV_flows) toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); - /* Both receive */ - int16_t storage[frame_size]; - int recved; - - /* Payload from Bob */ - recved = toxav_recv_audio(status_control.Alice.av, status_control.Alice.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Bob is invalid");*/ - } - - recved = toxav_recv_audio(status_control.Bob.av, status_control.Bob.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Alice is invalid");*/ - } - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ step++; /* This terminates the loop */ toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); @@ -320,38 +325,6 @@ START_TEST(test_AV_flows) // toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); - /* Both receive */ - int16_t storage[frame_size]; - vpx_image_t *video_storage; - int recved; - - /* Payload from Bob */ - recved = toxav_recv_audio(status_control.Alice.av, status_control.Alice.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Bob is invalid");*/ - } - - /* Video payload */ -// toxav_recv_video(status_control.Alice.av, status_control.Alice.call_index, &video_storage); -// -// if ( video_storage ) { -// /*ck_assert_msg( memcmp(video_storage->planes[VPX_PLANE_Y], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_U], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_V], sample_payload, 10) == 0 , "Payload from Bob is invalid");*/ -// vpx_img_free(video_storage); -// } - - - - - /* Payload from Alice */ - recved = toxav_recv_audio(status_control.Bob.av, status_control.Bob.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Alice is invalid");*/ - } - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ step++; /* This terminates the loop */ toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); @@ -391,48 +364,6 @@ START_TEST(test_AV_flows) // toxav_send_video(status_control.Alice.av, status_control.Alice.call_index, sample_image); // toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); - /* Both receive */ - int16_t storage[frame_size]; - vpx_image_t *video_storage; - int recved; - - /* Payload from Bob */ - recved = toxav_recv_audio(status_control.Alice.av, status_control.Alice.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Bob is invalid");*/ - } - - /* Video payload */ -// toxav_recv_video(status_control.Alice.av, status_control.Alice.call_index, &video_storage); -// -// if ( video_storage ) { -// /*ck_assert_msg( memcmp(video_storage->planes[VPX_PLANE_Y], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_U], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_V], sample_payload, 10) == 0 , "Payload from Bob is invalid");*/ -// vpx_img_free(video_storage); -// } - - - - - /* Payload from Alice */ - recved = toxav_recv_audio(status_control.Bob.av, status_control.Bob.call_index, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from Alice is invalid");*/ - } - - /* Video payload */ -// toxav_recv_video(status_control.Bob.av, status_control.Bob.call_index, &video_storage); -// -// if ( video_storage ) { -// /*ck_assert_msg( memcmp(video_storage->planes[VPX_PLANE_Y], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_U], sample_payload, 10) == 0 || -// memcmp(video_storage->planes[VPX_PLANE_V], sample_payload, 10) == 0 , "Payload from Alice is invalid");*/ -// vpx_img_free(video_storage); -// } - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ step++; /* This terminates the loop */ @@ -445,8 +376,49 @@ START_TEST(test_AV_flows) } TERMINATE_SCOPE() + + uint64_t times_they_are_a_changin = time(NULL); + /* Media change */ + CALL_AND_START_LOOP(TypeAudio, TypeAudio) { + /* Both send */ + payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, + 1000, sample_payload, frame_size); + + if ( payload_size < 0 ) { + ck_assert_msg ( 0, "Failed to encode payload" ); + } + + toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); + + payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, + sample_payload, frame_size); + + if ( payload_size < 0 ) { + ck_assert_msg ( 0, "Failed to encode payload" ); + } + + toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); + + /* Wait 2 seconds and change transmission type */ + if (time(NULL) - times_they_are_a_changin > 2) { + times_they_are_a_changin = time(NULL); + toxav_change_type(status_control.Alice.av, status_control.Alice.call_index, + toxav_get_peer_transmission_type(status_control.Bob.av, status_control.Bob.call_index, 0) + == TypeAudio ? TypeVideo : TypeAudio); + } + + if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ + step++; /* This terminates the loop */ + toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); + toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); + + /* Call over Alice hangs up */ + toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); + } + } + TERMINATE_SCOPE() - + /************************************************************************************************* * Other flows */ @@ -491,47 +463,78 @@ START_TEST(test_AV_flows) printf("\n"); } - + /* * Call and cancel */ { int step = 0; int running = 1; - + while (running) { tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); - + switch ( step ) { case 0: /* Alice */ printf("Alice is calling...\n"); toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, TypeAudio, 10); step++; break; - \ - + + case 1: /* Alice again */ if (status_control.Bob.status == Ringing) { printf("Alice cancels...\n"); toxav_cancel(status_control.Alice.av, status_control.Alice.call_index, 0, "Who likes D's anyway?"); step++; } - + break; - + case 2: /* Wait for Both to have status ended */ if (status_control.Bob.status == Cancel) running = 0; - + break; } - + c_sleep(20); } - + printf("\n"); } + + /* + * Timeout + */ + { + int step = 0; + int running = 1; + while (running) { + tox_do(bootstrap_node); + tox_do(Alice); + tox_do(Bob); + + switch ( step ) { + case 0: + printf("Alice is calling...\n"); + toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, TypeAudio, 10); + step++; + break; + + case 1: + if (status_control.Alice.status == TimedOut) running = 0; + break; + } + + c_sleep(20); + } + + printf("\n"); + } + + printf("Calls ended!\n"); diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c index 2a931cb0..b64caf10 100644 --- a/auto_tests/toxav_many_test.c +++ b/auto_tests/toxav_many_test.c @@ -24,6 +24,7 @@ #define c_sleep(x) usleep(1000*x) #endif +pthread_mutex_t muhmutex; typedef enum _CallStatus { none, @@ -43,6 +44,7 @@ typedef struct _Party { typedef struct _ACall { pthread_t tid; + int idx; Party Caller; Party Callee; @@ -52,6 +54,8 @@ typedef struct _Status { ACall calls[3]; /* Make 3 calls for this test */ } Status; +Status status_control; + void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata) { if (length == 7 && memcmp("gentoo", data, 7) == 0) { @@ -61,58 +65,49 @@ void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *dat /******************************************************************************/ -void callback_recv_invite ( int32_t call_index, void *_arg ) +void callback_recv_invite ( void* av, int32_t call_index, void *_arg ) { /* Status *cast = _arg; cast->calls[call_index].Callee.status = Ringing;*/ } -void callback_recv_ringing ( int32_t call_index, void *_arg ) +void callback_recv_ringing ( void* av, int32_t call_index, void *_arg ) { Status *cast = _arg; - cast->calls[call_index].Caller.status = Ringing; } -void callback_recv_starting ( int32_t call_index, void *_arg ) +void callback_recv_starting ( void* av, int32_t call_index, void *_arg ) { Status *cast = _arg; - cast->calls[call_index].Caller.status = InCall; } -void callback_recv_ending ( int32_t call_index, void *_arg ) +void callback_recv_ending ( void* av, int32_t call_index, void *_arg ) { Status *cast = _arg; - cast->calls[call_index].Caller.status = Ended; } -void callback_recv_error ( int32_t call_index, void *_arg ) -{ - ck_assert_msg(0, "AV internal error"); -} - -void callback_call_started ( int32_t call_index, void *_arg ) +void callback_call_started ( void* av, int32_t call_index, void *_arg ) { /* Status *cast = _arg; cast->calls[call_index].Callee.status = InCall;*/ } -void callback_call_canceled ( 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 ( int32_t call_index, void *_arg ) +void callback_call_rejected ( void* av, int32_t call_index, void *_arg ) { Status *cast = _arg; - cast->calls[call_index].Caller.status = Rejected; } -void callback_call_ended ( int32_t call_index, void *_arg ) +void callback_call_ended ( void* av, int32_t call_index, void *_arg ) { /* Status *cast = _arg; @@ -120,12 +115,40 @@ void callback_call_ended ( int32_t call_index, void *_arg ) cast->calls[call_index].Callee.status = Ended;*/ } -void callback_requ_timeout ( int32_t call_index, void *_arg ) +void callback_requ_timeout ( void* av, int32_t call_index, void *_arg ) { ck_assert_msg(0, "No answer!"); } + +static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length) +{ +} + +static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img) +{ +} + +void register_callbacks(ToxAv* av, void* data) +{ + toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); + toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, 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_recv_invite, av_OnInvite, 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_audio_recv_callback(av, callback_audio); + toxav_register_video_recv_callback(av, callback_video); +} /*************************************************************************************************/ +int call_running[3]; void *in_thread_call (void *arg) { @@ -133,18 +156,22 @@ void *in_thread_call (void *arg) ACall *this_call = arg; uint64_t start = 0; - int step = 0, running = 1; + int step = 0; int call_idx; - + + call_running[this_call->idx] = 1; + const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); int16_t sample_payload[frame_size]; randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; - + register_callbacks(this_call->Caller.av, &status_control); + register_callbacks(this_call->Callee.av, arg); + /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */ - while (running) { + while (call_running[this_call->idx]) { switch ( step ) { case 0: /* CALLER */ @@ -191,27 +218,15 @@ void *in_thread_call (void *arg) int16_t storage[RTP_PAYLOAD_SIZE]; int recved; - /* Payload from CALLER */ - recved = toxav_recv_audio(this_call->Callee.av, 0, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from CALLER is invalid");*/ - } - - /* Payload from CALLEE */ - recved = toxav_recv_audio(this_call->Caller.av, call_idx, frame_size, storage); - - if ( recved ) { - /*ck_assert_msg(recved == 10 && memcmp(storage, sample_payload, 10) == 0, "Payload from CALLEE is invalid");*/ - } - c_sleep(20); } step++; /* This terminates the loop */ + pthread_mutex_lock(&muhmutex); toxav_kill_transmission(this_call->Callee.av, 0); toxav_kill_transmission(this_call->Caller.av, call_idx); + pthread_mutex_unlock(&muhmutex); /* Call over CALLER hangs up */ toxav_hangup(this_call->Caller.av, call_idx); @@ -224,7 +239,7 @@ void *in_thread_call (void *arg) if (this_call->Caller.status == Ended) { c_sleep(1000); /* race condition */ this_call->Callee.status = Ended; - running = 0; + call_running[this_call->idx] = 0; } break; @@ -306,55 +321,38 @@ START_TEST(test_AV_three_calls) ToxAv *uniqcallerav = toxav_new(caller, 3); - Status status_control = { - 0, - {none, uniqcallerav, 0}, - {none, toxav_new(callees[0], 1), 0}, + for (i = 0; i < 3; i ++) { + status_control.calls[i].idx = i; + + status_control.calls[i].Caller.av = uniqcallerav; + status_control.calls[i].Caller.id = 0; + status_control.calls[i].Caller.status= none; + + status_control.calls[i].Callee.av = toxav_new(callees[i], 1); + status_control.calls[i].Callee.id = i; + status_control.calls[i].Callee.status= none; + } + + pthread_mutex_init(&muhmutex, NULL); - 0, - {none, uniqcallerav}, - {none, toxav_new(callees[1], 1), 1}, - - 0, - {none, uniqcallerav}, - {none, toxav_new(callees[2], 1), 2} - }; - - - toxav_register_callstate_callback(callback_call_started, av_OnStart, &status_control); - toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, &status_control); - toxav_register_callstate_callback(callback_call_rejected, av_OnReject, &status_control); - toxav_register_callstate_callback(callback_call_ended, av_OnEnd, &status_control); - toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, &status_control); - - toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, &status_control); - toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, &status_control); - toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, &status_control); - - toxav_register_callstate_callback(callback_recv_error, av_OnError, &status_control); - toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, &status_control); - - - - for ( i = 0; i < 3; i++ ) + for ( i = 0; i < 3; i++ ) pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]); - /* Now start 3 calls and they'll run for 10 s */ for ( i = 0; i < 3; i++ ) pthread_detach(status_control.calls[i].tid); - while ( - status_control.calls[0].Callee.status != Ended && status_control.calls[0].Caller.status != Ended && - status_control.calls[1].Callee.status != Ended && status_control.calls[1].Caller.status != Ended && - status_control.calls[2].Callee.status != Ended && status_control.calls[2].Caller.status != Ended - ) { + while (call_running[0] || call_running[1] || call_running[2]) { + pthread_mutex_lock(&muhmutex); + tox_do(bootstrap_node); tox_do(caller); tox_do(callees[0]); tox_do(callees[1]); tox_do(callees[2]); + + pthread_mutex_unlock(&muhmutex); c_sleep(20); } @@ -403,4 +401,4 @@ int main(int argc, char *argv[]) // test_AV_three_calls(); // // return 0; -} \ No newline at end of file +} diff --git a/toxav/codec.c b/toxav/codec.c index fbf78d69..ae24a976 100644 --- a/toxav/codec.c +++ b/toxav/codec.c @@ -81,10 +81,10 @@ JitterBuffer *create_queue(int capacity) void terminate_queue(JitterBuffer *q) { if (!q) return; - + empty_queue(q); free(q->queue); - + LOGGER_DEBUG("Terminated jitter buffer: %p", q); free(q); } @@ -217,6 +217,9 @@ int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_ if (cfg.g_w == width && cfg.g_h == height) return 0; + if (width * height > cs->max_width * cs->max_height) + return -1; + LOGGER_DEBUG("New video resolution: %u %u", width, height); cfg.g_w = width; cfg.g_h = height; @@ -249,7 +252,7 @@ int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate) return 0; } -int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t video_bitrate) +int init_video_encoder(CodecState *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); @@ -260,13 +263,18 @@ int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t } cfg.rc_target_bitrate = video_bitrate; - cfg.g_w = 8192; - cfg.g_h = 8192; + 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 = 300; + 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); @@ -282,9 +290,6 @@ int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t return -1; } - if (reconfigure_video_encoder_resolution(cs, width, height) != 0) - return -1; - return 0; } @@ -322,8 +327,8 @@ CodecState *codec_init_session ( uint32_t audio_bitrate, uint32_t audio_sample_rate, uint32_t audio_channels, uint32_t audio_VAD_tolerance_ms, - uint16_t video_width, - uint16_t video_height, + uint16_t max_video_width, + uint16_t max_video_height, uint32_t video_bitrate ) { CodecState *retu = calloc(sizeof(CodecState), 1); @@ -334,11 +339,12 @@ CodecState *codec_init_session ( uint32_t audio_bitrate, retu->audio_sample_rate = audio_sample_rate; /* Encoders */ - if (!video_width || !video_height) { /* Disable video */ + if (!max_video_width || !max_video_height) { /* Disable video */ /*video_width = 320; video_height = 240; */ } else { - retu->capabilities |= ( 0 == init_video_encoder(retu, video_width, video_height, video_bitrate) ) ? v_encoding : 0; + 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; } @@ -360,7 +366,7 @@ CodecState *codec_init_session ( uint32_t audio_bitrate, void codec_terminate_session ( CodecState *cs ) { if (!cs) return; - + if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); @@ -372,7 +378,7 @@ void codec_terminate_session ( CodecState *cs ) if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder); - + LOGGER_DEBUG("Terminated codec state: %p", cs); free(cs); } diff --git a/toxav/codec.h b/toxav/codec.h index d8e9f1a7..a464ec8f 100644 --- a/toxav/codec.h +++ b/toxav/codec.h @@ -56,6 +56,9 @@ typedef struct _CodecState { /* video decoding */ vpx_codec_ctx_t v_decoder; + int bitrate; + int max_width; + int max_height; /* audio encoding */ OpusEncoder *audio_encoder; diff --git a/toxav/msi.c b/toxav/msi.c index 999f86b6..f14729d2 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -1,4 +1,4 @@ -/** toxmsi.c +/** msi.c * * Copyright (C) 2013 Tox project All Rights Reserved. * @@ -34,19 +34,7 @@ #include #include -#define same(x, y) strcmp((const char*) x, (const char*) y) == 0 - -#define MSI_MAXMSG_SIZE 1024 - -#define TYPE_REQUEST 1 -#define TYPE_RESPONSE 2 - -unsigned char *VERSION_STRING = (unsigned char *)"0.3.1"; -#define VERSION_STRLEN 5 - -#define CT_AUDIO_HEADER_VALUE "AUDIO" -#define CT_VIDEO_HEADER_VALUE "VIDEO" - +#define MSI_MAXMSG_SIZE 256 /* Define default timeout for a request. * There is no behavior specified by the msi on what will @@ -57,31 +45,55 @@ unsigned char *VERSION_STRING = (unsigned char *)"0.3.1"; /** * Protocol: * - * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) | - * - * ie. - * - * | 0x1 | 0x0 0x7 | "version" - * - * Means: it's field value with length of 7 bytes and value of "version" - * It's similar to amp protocol + * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| */ -#define GENERIC_HEADER(header) \ +typedef enum { + IDRequest = 1, + IDResponse, + IDReason, + IDCallType, + IDCallId, + +} MSIHeaderID; + +typedef enum { + TypeRequest, + TypeResponse, + +} MSIMessageType; + +typedef enum { + invite, + start, + cancel, + reject, + end, + +} MSIRequest; + +typedef enum { + ringing, + starting, + ending, + error + +} MSIResponse; + + +#define GENERIC_HEADER(header, val_type) \ typedef struct _MSIHeader##header { \ -uint8_t* header_value; \ -uint16_t size; \ +val_type value; \ +_Bool exists; \ } MSIHeader##header; -GENERIC_HEADER ( Version ) -GENERIC_HEADER ( Request ) -GENERIC_HEADER ( Response ) -GENERIC_HEADER ( CallType ) -GENERIC_HEADER ( CallId ) -GENERIC_HEADER ( Info ) -GENERIC_HEADER ( Reason ) +GENERIC_HEADER ( Request, MSIRequest ) +GENERIC_HEADER ( Response, MSIResponse ) +GENERIC_HEADER ( CallType, MSICallType ) +GENERIC_HEADER ( CallId, MSICallIDType ) +GENERIC_HEADER ( Reason, MSIReasonStrType ) /** @@ -91,111 +103,25 @@ GENERIC_HEADER ( Reason ) */ typedef struct _MSIMessage { - MSIHeaderVersion version; MSIHeaderRequest request; MSIHeaderResponse response; MSIHeaderCallType calltype; - MSIHeaderInfo info; MSIHeaderReason reason; MSIHeaderCallId callid; - struct _MSIMessage *next; - int friend_id; } MSIMessage; -static struct _Callbacks { - MSICallback function; - void *data; -} callbacks[11] = {{0}}; - -inline__ void invoke_callback(int32_t call_index, MSICallbackID id) +inline__ void invoke_callback(MSISession* session, int32_t call_index, MSICallbackID id) { - if ( callbacks[id].function ) { + if ( session->callbacks[id].function ) { LOGGER_DEBUG("Invoking callback function: %d", id); - callbacks[id].function ( call_index, callbacks[id].data ); + session->callbacks[id].function ( session->agent_handler, call_index, session->callbacks[id].data ); } } -/*static MSICallback callbacks[10] = {0};*/ - - -/* define strings for the identifiers */ -#define VERSION_FIELD "Version" -#define REQUEST_FIELD "Request" -#define RESPONSE_FIELD "Response" -#define INFO_FIELD "INFO" -#define REASON_FIELD "Reason" -#define CALLTYPE_FIELD "Call-type" -#define CALLID_FIELD "Call-id" - -/* protocol descriptors */ -#define end_byte 0x0 -#define field_byte 0x1 -#define value_byte 0x2 - - -typedef enum { - invite, - start, - cancel, - reject, - end, - -} MSIRequest; - - -/** - * @brief Get string value for request. - * - * @param request The request. - * @return const uint8_t* The string - */ -static inline__ const uint8_t *stringify_request ( MSIRequest request ) -{ - static const uint8_t *strings[] = { - ( uint8_t *) "INVITE", - ( uint8_t *) "START", - ( uint8_t *) "CANCEL", - ( uint8_t *) "REJECT", - ( uint8_t *) "END" - }; - - return strings[request]; -} - - -typedef enum { - ringing, - starting, - ending, - error - -} MSIResponse; - - -/** - * @brief Get string value for response. - * - * @param response The response. - * @return const uint8_t* The string - */ -static inline__ const uint8_t *stringify_response ( MSIResponse response ) -{ - static const uint8_t *strings[] = { - ( uint8_t *) "ringing", - ( uint8_t *) "starting", - ( uint8_t *) "ending", - ( uint8_t *) "error" - }; - - return strings[response]; -} - - - /** * @brief Parse raw 'data' received from socket into MSIMessage struct. * Every message has to have end value of 'end_byte' or _undefined_ behavior @@ -209,124 +135,69 @@ static inline__ const uint8_t *stringify_response ( MSIResponse response ) */ int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) { - -#define ON_HEADER(iterator, size_con, header, descriptor, type_size_const) \ -( memcmp(iterator, descriptor, type_size_const) == 0){ /* Okay */ \ -iterator += type_size_const; /* Set iterator at begining of value part */ \ -if ( *iterator != value_byte || size_con <= type_size_const) { return -1; } size_con -= type_size_const; \ -iterator ++; if(size_con <= 3) {return -1;} size_con -= 3; \ -uint16_t _value_size; memcpy(&_value_size, iterator, sizeof(_value_size)); _value_size = ntohs(_value_size);\ -if(size_con < _value_size) { return -1; } size_con -= _value_size; \ -if ( !(header.header_value = calloc(sizeof(uint8_t), _value_size)) ) \ -LOGGER_ERROR("Allocation failed! Program might misbehave!"); \ -header.size = _value_size; \ -memcpy(header.header_value, iterator + 2, _value_size);\ -iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ } - + +#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } +#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } +#define FAIL_LIMITS(byte, low, high) if ( byte < low || byte > high ) { LOGGER_ERROR("Invalid data!"); return -1; } + if ( msg == NULL ) { LOGGER_ERROR("Could not parse message: no storage!"); return -1; } - - if ( data[length - 1] ) /* End byte must have value 0 */ + + if ( data[length - 1] ) { /* End byte must have value 0 */ + LOGGER_ERROR("Invalid end byte"); return -1; - - const uint8_t *_it = data; - uint16_t size_max = length; - - while ( *_it ) {/* until end_byte is hit */ - - uint16_t itedlen = (_it - data) + 2; - - if ( *_it == field_byte && itedlen < length ) { - - uint16_t _size; - memcpy(&_size, _it + 1, sizeof(_size)); - _size = ntohs(_size); - - if ( itedlen + _size > length ) return -1; - - _it += 3; /* place it at the field value beginning */ - size_max -= 3; - - switch ( _size ) { /* Compare the size of the hardcoded values ( very convenient ) */ - - case 4: { /* INFO header */ - if ON_HEADER ( _it, size_max, msg->info, INFO_FIELD, 4 ) - } - break; - - case 6: { /* Reason header */ - if ON_HEADER ( _it, size_max, msg->reason, REASON_FIELD, 6 ) - } - break; - - case 7: { /* Version, Request, Call-id headers */ - if ON_HEADER ( _it, size_max, msg->version, VERSION_FIELD, 7 ) - else if ON_HEADER ( _it, size_max, msg->request, REQUEST_FIELD, 7 ) - else if ON_HEADER ( _it, size_max, msg->callid, CALLID_FIELD, 7 ) - } - break; - - case 8: { /* Response header */ - if ON_HEADER ( _it, size_max, msg->response, RESPONSE_FIELD, 8 ) - } - break; - - case 9: { /* Call-type header */ - if ON_HEADER ( _it, size_max, msg->calltype, CALLTYPE_FIELD, 9 ) - } - break; - - default: - LOGGER_ERROR("Unkown field value"); - return -1; - } - } else { - LOGGER_ERROR("Invalid field byte or field size too large"); + } + + const uint8_t *it = data; + int size_constraint = length; + + while ( *it ) {/* until end byte is hit */ + switch (*it) { + case IDRequest: + FAIL_CONSTRAINT(size_constraint, 3); + FAIL_SIZE(it[1], 1); + FAIL_LIMITS(it[2], invite, end); + msg->request.value = it[2]; it += 3; + msg->request.exists = 1; + break; + case IDResponse: + FAIL_CONSTRAINT(size_constraint, 3); + FAIL_SIZE(it[1], 1); + FAIL_LIMITS(it[2], ringing, error); + msg->response.value = it[2]; it += 3; + msg->response.exists = 1; + break; + case IDCallType: + FAIL_CONSTRAINT(size_constraint, 3); + FAIL_SIZE(it[1], 1); + FAIL_LIMITS(it[2], type_audio, type_video); + msg->calltype.value = it[2]; it += 3; + msg->calltype.exists = 1; + break; + case IDCallId: + FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2); + FAIL_SIZE(it[1], sizeof(MSICallIDType)); + memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType)); it += sizeof(MSICallIDType) + 2; + msg->callid.exists = 1; + break; + case IDReason: + FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2); + FAIL_SIZE(it[1], sizeof(MSIReasonStrType)); + memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType)); it += sizeof(MSIReasonStrType) + 2; + msg->reason.exists = 1; + break; + default: + LOGGER_ERROR("Invalid id byte"); return -1; + break; } - - /* If it's anything else return failure as the message is invalid */ - } return 0; } - -#define ALLOCATE_HEADER( var, mheader_value, t_size) \ -if (!(var.header_value = calloc(sizeof *mheader_value, t_size))) \ -{ LOGGER_WARNING("Header allocation failed! Program might misbehave!"); } \ -else { memcpy(var.header_value, mheader_value, t_size); \ -var.size = t_size; } - - -/** - * @brief Speaks for it self. - * - * @param msg The message. - * @return void - */ -void free_message ( MSIMessage *msg ) -{ - if ( msg == NULL ) { - LOGGER_WARNING("Tried to free empty message"); - return; - } - - free ( msg->calltype.header_value ); - free ( msg->request.header_value ); - free ( msg->response.header_value ); - free ( msg->version.header_value ); - free ( msg->info.header_value ); - free ( msg->reason.header_value ); - free ( msg->callid.header_value ); - - free ( msg ); -} - - /** * @brief Create the message. * @@ -335,29 +206,25 @@ void free_message ( MSIMessage *msg ) * @return MSIMessage* Created message. * @retval NULL Error occurred. */ -MSIMessage *msi_new_message ( uint8_t type, const uint8_t *type_id ) +MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) { - MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 ); + MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); - if ( _retu == NULL ) { + if ( retu == NULL ) { LOGGER_WARNING("Allocation failed! Program might misbehave!"); return NULL; } - - if ( type == TYPE_REQUEST ) { - ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char *)type_id ) ) - - } else if ( type == TYPE_RESPONSE ) { - ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char *)type_id ) ) + + if ( type == TypeRequest ) { + retu->request.exists = 1; + retu->request.value = type_value; } else { - free_message ( _retu ); - return NULL; + retu->response.exists = 1; + retu->response.value = type_value; } - ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char *)VERSION_STRING ) ) - - return _retu; + return retu; } @@ -368,36 +235,27 @@ MSIMessage *msi_new_message ( uint8_t type, const uint8_t *type_id ) * @return MSIMessage* Parsed message. * @retval NULL Error occurred. */ -MSIMessage *parse_message ( const uint8_t *data, uint16_t length ) +MSIMessage *parse_recv ( const uint8_t *data, uint16_t length ) { if ( data == NULL ) { LOGGER_WARNING("Tried to parse empty message!"); return NULL; } - MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 ); + MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); - if ( _retu == NULL ) { + if ( retu == NULL ) { LOGGER_WARNING("Allocation failed! Program might misbehave!"); return NULL; } - memset ( _retu, 0, sizeof ( MSIMessage ) ); + if ( parse_raw_data ( retu, data, length ) == -1 ) { - if ( parse_raw_data ( _retu, data, length ) == -1 ) { - - free_message ( _retu ); + free ( retu ); return NULL; } - if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size || - memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) { - - free_message ( _retu ); - return NULL; - } - - return _retu; + return retu; } @@ -411,135 +269,107 @@ MSIMessage *parse_message ( const uint8_t *data, uint16_t length ) * @param length Pointer to container length. * @return uint8_t* Iterated container. */ -uint8_t *append_header_to_string ( - uint8_t *dest, - const uint8_t *header_field, - const uint8_t *header_value, - uint16_t value_len, - uint16_t *length ) +uint8_t *format_output ( uint8_t *dest, MSIHeaderID id, const void *value, uint8_t value_len, uint16_t *length ) { if ( dest == NULL ) { LOGGER_ERROR("No destination space!"); return NULL; } - if (header_value == NULL) { + if (value == NULL || value_len == 0) { LOGGER_ERROR("Empty header value"); return NULL; } - if ( header_field == NULL ) { - LOGGER_ERROR("Empty header field"); - return NULL; - } - - - const uint8_t *_hvit = header_value; - uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/ - - *dest = field_byte; /* Set the first byte */ - - uint8_t *_getback_byte = dest + 1; /* remember the byte we were on */ - dest += 3; /* swith to 4th byte where field value starts */ - - /* Now set the field value and calculate it's length */ - uint16_t _i = 0; - - for ( ; header_field[_i]; ++_i ) { - *dest = header_field[_i]; - ++dest; - }; - - _total += _i; - - /* Now set the length of the field byte */ - uint16_t _convert; - - - _convert = htons(_i); - - memcpy(_getback_byte, &_convert, sizeof(_convert)); - - /* for value part do it regulary */ - *dest = value_byte; - - dest++; - - - _convert = htons(value_len); - - memcpy(dest, &_convert, sizeof(_convert)); - - dest += 2; - - for ( _i = value_len; _i; --_i ) { - *dest = *_hvit; - ++_hvit; - ++dest; - } - - *length += _total; - return dest; + *dest = id; dest ++; + *dest = value_len; dest ++; + + memcpy(dest, value, value_len); + + *length += (2 + value_len); + + return dest + value_len; /* Set to next position ready to be written */ } /** - * @brief Convert MSIMessage struct to _sendable_ string. + * @brief Parse MSIMessage to send. * * @param msg The message. * @param dest Destination. - * @return uint16_t It's final size. + * @return uint16_t Its final size. */ -uint16_t message_to_send ( MSIMessage *msg, uint8_t *dest ) +uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) { -#define CLEAN_ASSIGN(added, var, field, header)\ - if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); } - if (msg == NULL) { - LOGGER_ERROR("Empty message!"); + LOGGER_ERROR("No message!"); return 0; } if (dest == NULL ) { - LOGGER_ERROR("Empty destination!"); + LOGGER_ERROR("No destination!"); return 0; } - uint8_t *_iterated = dest; - uint16_t _size = 0; + uint8_t *it = dest; + uint16_t size = 0; + + if (msg->request.exists) { + uint8_t cast = msg->request.value; + it = format_output(it, IDRequest, &cast, 1, &size); + } + + if (msg->response.exists) { + uint8_t cast = msg->response.value; + it = format_output(it, IDResponse, &cast, 1, &size); + } + + if (msg->calltype.exists) { + uint8_t cast = msg->calltype.value; + it = format_output(it, IDCallType, &cast, 1, &size); + } + + if (msg->callid.exists) { + it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); + } + + if (msg->reason.exists) { + it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size); + } + + *it = 0; + size ++; - CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version ); - CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request ); - CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response ); - CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype ); - CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info ); - CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid ); - CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason ); - - *_iterated = end_byte; - _size ++; - - return _size; + return size; } -#define GENERIC_SETTER_DEFINITION(header) \ -void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \ -{ if ( !_msg || !header_value) { LOGGER_WARNING("No setter values!"); return; } \ - free(_msg->header.header_value); \ - ALLOCATE_HEADER( _msg->header, header_value, _size )} - -GENERIC_SETTER_DEFINITION ( calltype ) -GENERIC_SETTER_DEFINITION ( reason ) -GENERIC_SETTER_DEFINITION ( info ) -GENERIC_SETTER_DEFINITION ( callid ) +void msi_msg_set_calltype ( MSIMessage* msg, const MSICallType value ) +{ + if ( !msg ) return; + msg->calltype.exists = 1; + msg->calltype.value = value; +} +void msi_msg_set_reason ( MSIMessage* msg, const MSIReasonStrType value ) +{ + if ( !msg ) return; + msg->reason.exists = 1; + memcpy(msg->reason.value, value, sizeof(MSIReasonStrType)); +} +void msi_msg_set_callid ( MSIMessage* msg, const MSICallIDType value ) +{ + if ( !msg ) return; + msg->callid.exists = 1; + memcpy(msg->callid.value, value, sizeof(MSICallIDType)); +} typedef struct _Timer { void *(*func)(void *); - void *func_args; + void *func_arg1; + int func_arg2; uint64_t timeout; size_t idx; @@ -557,6 +387,10 @@ typedef struct _TimerHandler { } TimerHandler; +struct timer_function_args { + void *arg1; + int arg2; +}; /** * @brief Allocate timer in array @@ -567,8 +401,9 @@ typedef struct _TimerHandler { * @param timeout Timeout in ms * @return int */ -int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *arg, unsigned timeout) +int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *arg1, int arg2, unsigned timeout) { + static int timer_id; pthread_mutex_lock(&timers_container->mutex); int i = 0; @@ -592,9 +427,11 @@ int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *ar timers_container->size ++; timer->func = func; - timer->func_args = arg; + timer->func_arg1 = arg1; + timer->func_arg2 = arg2; timer->timeout = timeout + current_time_monotonic(); /* In ms */ - timer->idx = i; + ++timer_id; + timer->idx = timer_id; /* reorder */ if (i) { @@ -610,35 +447,46 @@ int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *ar pthread_mutex_unlock(&timers_container->mutex); LOGGER_DEBUG("Allocated timer index: %d timeout: %d, current size: %d", i, timeout, timers_container->size); - return i; + return timer->idx; } /** * @brief Remove timer from array * * @param timers_container handler - * @param idx index + * @param id timer id + * @param lock_mutex (does the mutex need to be locked) * @return int */ -int timer_release ( TimerHandler *timers_container, int idx ) +int timer_release ( TimerHandler *timers_container, int id , int lock_mutex) { - int rc = pthread_mutex_trylock(&timers_container->mutex); + if (lock_mutex) + pthread_mutex_lock(&timers_container->mutex); Timer **timed_events = timers_container->timers; - if (!timed_events[idx]) { - LOGGER_WARNING("No event under index: %d", idx); + int i, res = -1; - if ( rc != EBUSY ) pthread_mutex_unlock(&timers_container->mutex); + for (i = 0; i < timers_container->max_capacity; ++i) { + if (timed_events[i] && timed_events[i]->idx == id) { + res = i; + break; + } + } + + if (res == -1) { + LOGGER_WARNING("No event with id: %d", id); + + if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); return -1; } - free(timed_events[idx]); + free(timed_events[res]); - timed_events[idx] = NULL; + timed_events[res] = NULL; - int i = idx + 1; + i = res + 1; for (; i < timers_container->max_capacity && timed_events[i]; i ++) { timed_events[i - 1] = timed_events[i]; @@ -647,9 +495,9 @@ int timer_release ( TimerHandler *timers_container, int idx ) timers_container->size--; - LOGGER_DEBUG("Popped index: %d, current size: %d ", idx, timers_container->size); + LOGGER_DEBUG("Popped id: %d, current size: %d ", id, timers_container->size); - if ( rc != EBUSY ) pthread_mutex_unlock(&timers_container->mutex); + if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); return 0; } @@ -673,16 +521,21 @@ void *timer_poll( void *arg ) uint64_t time = current_time_monotonic(); while ( handler->timers[0] && handler->timers[0]->timeout < time ) { + pthread_t tid; - pthread_t _tid; + struct timer_function_args *args = malloc(sizeof(struct timer_function_args)); + args->arg1 = handler->timers[0]->func_arg1; + args->arg2 = handler->timers[0]->func_arg2; - if ( 0 != pthread_create(&_tid, NULL, handler->timers[0]->func, handler->timers[0]->func_args) || - 0 != pthread_detach(_tid) ) + if ( 0 != pthread_create(&tid, NULL, handler->timers[0]->func, args) || + 0 != pthread_detach(tid) ) { LOGGER_ERROR("Failed to execute timer at: %d!", handler->timers[0]->timeout); + free(args); + } else { + LOGGER_DEBUG("Exectued timer assigned at: %d", handler->timers[0]->timeout); + } - else LOGGER_DEBUG("Exectued timer assigned at: %d", handler->timers[0]->timeout); - - timer_release(handler, 0); + timer_release(handler, handler->timers[0]->idx, 0); } } @@ -809,9 +662,9 @@ typedef enum { * @param error_code The code. * @return const uint8_t* The string. */ -static inline__ const uint8_t *stringify_error ( MSICallError error_code ) +static inline__ const uint8_t* stringify_error ( MSICallError error_code ) { - static const uint8_t *strings[] = { + static const uint8_t* strings[] = { ( uint8_t *) "", ( uint8_t *) "Using dead call", ( uint8_t *) "Call id not set to any call", @@ -824,29 +677,6 @@ static inline__ const uint8_t *stringify_error ( MSICallError error_code ) return strings[error_code]; } - -/** - * @brief Convert error_code into string. - * - * @param error_code The code. - * @return const uint8_t* The string. - */ -static inline__ const uint8_t *stringify_error_code ( MSICallError error_code ) -{ - static const uint8_t *strings[] = { - ( uint8_t *) "", - ( uint8_t *) "1", - ( uint8_t *) "2", - ( uint8_t *) "3", - ( uint8_t *) "4", - ( uint8_t *) "5", - ( uint8_t *) "6" - }; - - return strings[error_code]; -} - - /** * @brief Speaks for it self. * @@ -859,17 +689,17 @@ static inline__ const uint8_t *stringify_error_code ( MSICallError error_code ) */ int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) { - msi_msg_set_callid ( msg, call->id, CALL_ID_LEN ); + msi_msg_set_callid ( msg, call->id ); - uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; - uint16_t _length = message_to_send ( msg, _msg_string_final ); + uint8_t msg_string_final [MSI_MAXMSG_SIZE]; + uint16_t length = parse_send ( msg, msg_string_final ); - if (!_length) { + if (!length) { LOGGER_WARNING("Parsing message failed; nothing sent!"); return -1; } - if ( m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ) { + if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { LOGGER_DEBUG("Sent message"); return 0; } @@ -877,6 +707,12 @@ int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t return -1; } +inline__ int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to ) +{ + MSIMessage *msg = msi_new_message ( TypeResponse, response ); + send_message ( session, call, msg, to ); + free ( msg ); +} /** * @brief Determine 'bigger' call id @@ -889,7 +725,7 @@ int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t */ int call_id_bigger( const uint8_t *first, const uint8_t *second) { - return (memcmp(first, second, CALL_ID_LEN) < 0); + return (memcmp(first, second, sizeof(MSICallIDType)) < 0); } @@ -901,21 +737,15 @@ int call_id_bigger( const uint8_t *first, const uint8_t *second) * @param peer_id The peer. * @return void */ -void flush_peer_type ( MSICall *call, MSIMessage *msg, int peer_id ) +int flush_peer_type ( MSICall *call, MSIMessage *msg, int peer_id ) { - if ( msg->calltype.header_value ) { - uint8_t hdrval [MSI_MAXMSG_SIZE]; /* Make sure no overflow */ + if ( msg->calltype.exists ) { + call->type_peer[peer_id] = msg->calltype.value; + return 0; + } - memcpy(hdrval, msg->calltype.header_value, msg->calltype.size); - hdrval[msg->calltype.size] = '\0'; - - if ( strcmp ( ( const char *) hdrval, CT_AUDIO_HEADER_VALUE ) == 0 ) { - call->type_peer[peer_id] = type_audio; - - } else if ( strcmp ( ( const char *) hdrval, CT_VIDEO_HEADER_VALUE ) == 0 ) { - call->type_peer[peer_id] = type_video; - } else {} /* Error */ - } else {} /* Error */ + LOGGER_WARNING("No call type header!"); + return -1; } void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p) @@ -934,7 +764,8 @@ void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8 for ( ; i < session->calls[j]->peer_count; i ++ ) if ( session->calls[j]->peers[i] == friend_num ) { - invoke_callback(j, MSI_OnPeerTimeout); + invoke_callback(session, j, MSI_OnPeerTimeout); + terminate_call(session, session->calls[j]); LOGGER_DEBUG("Remote: %d timed out!", friend_num); return; /* TODO: On group calls change behaviour */ } @@ -954,12 +785,7 @@ MSICall *find_call ( MSISession *session, uint8_t *call_id ) uint32_t i = 0; for (; i < session->max_calls; i ++ ) - if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, CALL_ID_LEN) == 0 ) { - LOGGER_SCOPE( - char tmp[CALL_ID_LEN + 1] = {'\0'}; - memcpy(tmp, session->calls[i]->id, CALL_ID_LEN); - LOGGER_DEBUG("Found call id: %s", tmp); - ); + if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) { return session->calls[i]; } @@ -984,18 +810,11 @@ int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_ LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); - MSIMessage *_msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) ); + MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); - const uint8_t *_error_code_str = stringify_error_code ( errid ); - - msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char *) _error_code_str ) ); - send_message ( session, call, _msg_error, to ); - free_message ( _msg_error ); - - session->last_error_id = errid; - session->last_error_str = stringify_error ( errid ); - - /* invoke_callback(call->call_idx, MSI_OnError); */ + msi_msg_set_reason ( msg_error, stringify_error(errid) ); + send_message ( session, call, msg_error, to ); + free ( msg_error ); return 0; } @@ -1104,15 +923,13 @@ int terminate_call ( MSISession *session, MSICall *call ) return -1; } - int rc = pthread_mutex_trylock(&session->mutex); /* Lock if not locked */ - LOGGER_DEBUG("Terminated call id: %d", call->call_idx); /* Check event loop and cancel timed events if there are any * NOTE: This has to be done before possibly * locking the mutex the second time */ - timer_release ( session->timer_handler, call->request_timer_id ); - timer_release ( session->timer_handler, call->ringing_timer_id ); + timer_release ( session->timer_handler, call->request_timer_id, 1); + timer_release ( session->timer_handler, call->ringing_timer_id, 1); /* Get a handle */ pthread_mutex_lock ( &call->mutex ); @@ -1129,9 +946,6 @@ int terminate_call ( MSISession *session, MSICall *call ) free ( call ); - if ( rc != EBUSY ) /* Unlock if locked by this call */ - pthread_mutex_unlock(&session->mutex); - return 0; } @@ -1148,23 +962,27 @@ void *handle_timeout ( void *arg ) * timers on these cancels and terminate call on * their timeout */ - MSICall *_call = arg; + struct timer_function_args *args = arg; + int call_index = args->arg2; + MSISession *session = args->arg1; + MSICall *call = session->calls[call_index]; - if (_call) { - LOGGER_DEBUG("[Call: %s] Request timed out!", _call->id); + if (call) { + LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); - invoke_callback(_call->call_idx, MSI_OnRequestTimeout); + invoke_callback(session, call_index, MSI_OnRequestTimeout); } - if ( _call && _call->session ) { + if ( call && call->session ) { /* TODO: Cancel all? */ /* uint16_t _it = 0; * for ( ; _it < _session->call->peer_count; _it++ ) */ - msi_cancel ( _call->session, _call->call_idx, _call->peers [0], "Request timed out" ); - /*terminate_call(_call->session, _call);*/ + msi_cancel ( call->session, call->call_idx, call->peers [0], "Request timed out" ); + /*terminate_call(call->session, call);*/ } + free(arg); pthread_exit(NULL); } @@ -1172,38 +990,60 @@ void *handle_timeout ( void *arg ) /********** Request handlers **********/ int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) { - LOGGER_DEBUG("Session: %p Handling 'invite' on call: %s", session, call ? (char *)call->id : "making new"); + LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); pthread_mutex_lock(&session->mutex); - + + if (!msg->calltype.exists) {/**/ + LOGGER_WARNING("Peer sent invalid call type!"); + send_error ( session, call, error_no_callid, msg->friend_id ); + pthread_mutex_unlock(&session->mutex); + return 0; + } if ( call ) { if ( call->peers[0] == msg->friend_id ) { - /* The glare case. A calls B when at the same time - * B calls A. Who has advantage is set bey calculating - * 'bigger' Call id and then that call id is being used in - * future. User with 'bigger' Call id has the advantage - * as in he will wait the response from the other. - */ + if (call->state == call_inviting) { + /* The glare case. A calls B when at the same time + * B calls A. Who has advantage is set bey calculating + * 'bigger' Call id and then that call id is being used in + * future. User with 'bigger' Call id has the advantage + * as in he will wait the response from the other. + */ + LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]); + + if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */ - if ( call_id_bigger (call->id, msg->callid.header_value) == 1 ) { /* Peer has advantage */ + /* Terminate call; peer will timeout(call) if call initialization fails */ + terminate_call(session, call); - /* Terminate call; peer will timeout(call) if call initialization (magically) fails */ - terminate_call(session, call); + call = init_call ( session, 1, 0 ); - call = init_call ( session, 1, 0 ); + if ( !call ) { + pthread_mutex_unlock(&session->mutex); + LOGGER_ERROR("Starting call"); + return 0; + } - if ( !call ) { + } else { + pthread_mutex_unlock(&session->mutex); + return 0; /* Wait for ringing from peer */ + } + } else if (call->state == call_active) { + /* Request for media change; call callback and send starting response */ + if (flush_peer_type(call, msg, 0) != 0) { /**/ + LOGGER_WARNING("Peer sent invalid call type!"); + send_error ( session, call, error_no_callid, msg->friend_id ); pthread_mutex_unlock(&session->mutex); - LOGGER_ERROR("Starting call"); return 0; } - - } else { + + LOGGER_DEBUG("Set new call type: %s", call->type_peer[0] == type_audio ? "audio" : "video"); + send_reponse(session, call, starting, msg->friend_id); pthread_mutex_unlock(&session->mutex); - return 0; /* Wait for ringing from peer */ + invoke_callback(session, call->call_idx, MSI_OnMediaChange); + return 1; } - } else { send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ terminate_call(session, call); @@ -1220,27 +1060,25 @@ int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) } } - if ( !msg->callid.header_value ) { + if ( !msg->callid.exists ) { send_error ( session, call, error_no_callid, msg->friend_id ); terminate_call(session, call); pthread_mutex_unlock(&session->mutex); return 0; } - memcpy ( call->id, msg->callid.header_value, CALL_ID_LEN ); + memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); call->state = call_starting; add_peer( call, msg->friend_id); flush_peer_type ( call, msg, 0 ); - MSIMessage *_msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) ); - send_message ( session, call, _msg_ringing, msg->friend_id ); - free_message ( _msg_ringing ); + send_reponse(session, call, ringing, msg->friend_id); pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnInvite); + invoke_callback(session, call->call_idx, MSI_OnInvite); return 1; } @@ -1251,17 +1089,15 @@ int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - LOGGER_DEBUG("Session: %p Handling 'start' on call: %s, friend id: %d", session, call->id, msg->friend_id ); + LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); pthread_mutex_lock(&session->mutex); call->state = call_active; - flush_peer_type ( call, msg, 0 ); - pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnStart); + invoke_callback(session, call->call_idx, MSI_OnStart); return 1; } int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg ) @@ -1271,20 +1107,17 @@ int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - LOGGER_DEBUG("Session: %p Handling 'reject' on call: %s", session, call->id); + LOGGER_DEBUG("Session: %p Handling 'reject' on call: %s", session, call->call_idx); + invoke_callback(session, call->call_idx, MSI_OnReject); + pthread_mutex_lock(&session->mutex); - MSIMessage *_msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) ); - send_message ( session, call, _msg_ending, msg->friend_id ); - free_message ( _msg_ending ); - + send_reponse(session, call, ending, msg->friend_id); + terminate_call(session, call); pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnReject); - - terminate_call(session, call); return 1; } int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) @@ -1294,16 +1127,16 @@ int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %s", session, call->id ); + LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %s", session, call->call_idx); + + invoke_callback(session, call->call_idx, MSI_OnCancel); pthread_mutex_lock(&session->mutex); - - /* Act as end message */ - - pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnCancel); - + terminate_call ( session, call ); + + pthread_mutex_unlock(&session->mutex); + return 1; } int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg ) @@ -1313,19 +1146,17 @@ int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - LOGGER_DEBUG("Session: %p Handling 'end' on call: %s", session, call->id ); + LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); + invoke_callback(session, call->call_idx, MSI_OnEnd); pthread_mutex_lock(&session->mutex); - MSIMessage *_msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) ); - send_message ( session, call, _msg_ending, msg->friend_id ); - free_message ( _msg_ending ); - + send_reponse(session, call, ending, msg->friend_id); + terminate_call ( session, call ); + pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnEnd); - terminate_call ( session, call ); return 1; } @@ -1345,39 +1176,57 @@ int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %s", session, call->id ); + LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx ); - call->ringing_timer_id = timer_alloc ( session->timer_handler, handle_timeout, call, call->ringing_tout_ms ); + call->ringing_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, + call->ringing_tout_ms ); pthread_mutex_unlock(&session->mutex); - invoke_callback(call->call_idx, MSI_OnRinging); + invoke_callback(session, call->call_idx, MSI_OnRinging); return 1; } int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) { if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); + LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call"); return 0; } pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Handling 'starting' on call: %s", session, call->id ); + if ( call->state == call_active ) { /* Change media */ + + LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); + pthread_mutex_unlock(&session->mutex); + + invoke_callback(session, call->call_idx, MSI_OnMediaChange); + + } else if ( call->state == call_inviting ) { + LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); - call->state = call_active; + call->state = call_active; - MSIMessage *_msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) ); - send_message ( session, call, _msg_start, msg->friend_id ); - free_message ( _msg_start ); + MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); + send_message ( session, call, msg_start, msg->friend_id ); + free ( msg_start ); - flush_peer_type ( call, msg, 0 ); + + flush_peer_type ( call, msg, 0 ); + /* This is here in case of glare */ + timer_release ( session->timer_handler, call->ringing_timer_id, 1 ); + + pthread_mutex_unlock(&session->mutex); - timer_release ( session->timer_handler, call->ringing_timer_id ); - pthread_mutex_unlock(&session->mutex); - - invoke_callback(call->call_idx, MSI_OnStarting); + invoke_callback(session, call->call_idx, MSI_OnStarting); + } else { + LOGGER_ERROR("Invalid call state"); + terminate_call(session, call ); + pthread_mutex_unlock(&session->mutex); + return 0; + } + return 1; } int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg ) @@ -1387,25 +1236,19 @@ int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg ) return 0; } - pthread_mutex_lock(&session->mutex); + LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); - LOGGER_DEBUG("Session: %p Handling 'ending' on call: %s", session, call->id ); - - /* Stop timer */ - timer_release ( session->timer_handler, call->request_timer_id ); - - pthread_mutex_unlock(&session->mutex); - - invoke_callback(call->call_idx, MSI_OnEnding); + invoke_callback(session, call->call_idx, MSI_OnEnding); /* Terminate call */ + pthread_mutex_lock(&session->mutex); terminate_call ( session, call ); + pthread_mutex_unlock(&session->mutex); return 1; } int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) { - pthread_mutex_lock(&session->mutex); if ( !call ) { LOGGER_WARNING("Handling 'error' on non-existing call!"); @@ -1413,20 +1256,19 @@ int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) return -1; } - LOGGER_DEBUG("Session: %p Handling 'error' on call: %s", session, call->id ); + LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); + invoke_callback(session, call->call_idx, MSI_OnEnding); + + pthread_mutex_lock(&session->mutex); /* Handle error accordingly */ - if ( msg->reason.header_value ) { - session->last_error_id = atoi ( ( const char *) msg->reason.header_value ); - session->last_error_str = stringify_error ( session->last_error_id ); - LOGGER_DEBUG("Error reason: %s", session->last_error_str); + if ( msg->reason.exists ) { + /* TODO */ } - pthread_mutex_unlock(&session->mutex); - - invoke_callback(call->call_idx, MSI_OnEnding); - terminate_call ( session, call ); + + pthread_mutex_unlock(&session->mutex); return 1; } @@ -1478,7 +1320,7 @@ void msi_handle_packet ( Messenger *messenger, int source, const uint8_t *data, return; } - msg = parse_message ( data, length ); + msg = parse_recv ( data, length ); if ( !msg ) { LOGGER_WARNING("Error parsing message"); @@ -1491,79 +1333,38 @@ void msi_handle_packet ( Messenger *messenger, int source, const uint8_t *data, /* Find what call */ - MSICall *call = msg->callid.header_value ? find_call(session, msg->callid.header_value ) : NULL; + MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; /* Now handle message */ - if ( msg->request.header_value ) { /* Handle request */ - - if ( msg->response.size > 32 ) { - LOGGER_WARNING("Header size too big"); - goto free_end; - } - - uint8_t _request_value[32]; - - memcpy(_request_value, msg->request.header_value, msg->request.size); - _request_value[msg->request.size] = '\0'; - - if ( same ( _request_value, stringify_request ( invite ) ) ) { - handle_recv_invite ( session, call, msg ); - - } else if ( same ( _request_value, stringify_request ( start ) ) ) { - handle_recv_start ( session, call, msg ); - - } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { - handle_recv_cancel ( session, call, msg ); - - } else if ( same ( _request_value, stringify_request ( reject ) ) ) { - handle_recv_reject ( session, call, msg ); - - } else if ( same ( _request_value, stringify_request ( end ) ) ) { - handle_recv_end ( session, call, msg ); - } else { - LOGGER_WARNING("Uknown request"); - goto free_end; - } - - } else if ( msg->response.header_value ) { /* Handle response */ - - if ( msg->response.size > 32 ) { - LOGGER_WARNING("Header size too big"); - goto free_end; + if ( msg->request.exists ) { /* Handle request */ + + switch(msg->request.value) { + case invite: handle_recv_invite ( session, call, msg ); break; + case start: handle_recv_start ( session, call, msg ); break; + case cancel: handle_recv_cancel ( session, call, msg ); break; + case reject: handle_recv_reject ( session, call, msg ); break; + case end: handle_recv_end ( session, call, msg ); break; } + + } else if ( msg->response.exists ) { /* Handle response */ /* Got response so cancel timer */ - if ( call ) timer_release ( session->timer_handler, call->request_timer_id ); - - uint8_t _response_value[32]; - - memcpy(_response_value, msg->response.header_value, msg->response.size); - _response_value[msg->response.size] = '\0'; - - if ( same ( _response_value, stringify_response ( ringing ) ) ) { - handle_recv_ringing ( session, call, msg ); - - } else if ( same ( _response_value, stringify_response ( starting ) ) ) { - handle_recv_starting ( session, call, msg ); - - } else if ( same ( _response_value, stringify_response ( ending ) ) ) { - handle_recv_ending ( session, call, msg ); - - } else if ( same ( _response_value, stringify_response ( error ) ) ) { - handle_recv_error ( session, call, msg ); - - } else { - LOGGER_WARNING("Uknown response"); - goto free_end; + if ( call ) timer_release ( session->timer_handler, call->request_timer_id, 1 ); + + switch(msg->response.value) { + case ringing: handle_recv_ringing ( session, call, msg ); break; + case starting: handle_recv_starting ( session, call, msg ); break; + case ending: handle_recv_ending ( session, call, msg ); break; + case error: handle_recv_error ( session, call, msg ); break; } - + } else { LOGGER_WARNING("Invalid message: no resp nor requ headers"); } free_end: - free_message ( msg ); + free ( msg ); } @@ -1574,10 +1375,10 @@ free_end: * @param id The id. * @return void */ -void msi_register_callback ( MSICallback callback, MSICallbackID id, void *userdata ) +void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata ) { - callbacks[id].function = callback; - callbacks[id].data = userdata; + session->callbacks[id].function = callback; + session->callbacks[id].data = userdata; } @@ -1624,7 +1425,7 @@ MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ) retu->frequ = 10000; /* default value? */ retu->call_timeout = 30000; /* default value? */ - + m_callback_msi_packet(messenger, msi_handle_packet, retu ); @@ -1695,37 +1496,40 @@ int msi_invite ( MSISession *session, int32_t *call_index, MSICallType call_type LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); - MSICall *_call = init_call ( session, 1, rngsec ); /* Just one peer for now */ + int i = 0; + for (; i < session->max_calls; i ++) + if (session->calls[i] && session->calls[i]->peers[0] == friend_id) { + LOGGER_ERROR("Already in a call with friend %d", friend_id); + pthread_mutex_unlock(&session->mutex); + return -1; + } + + + MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ - if ( !_call ) { + if ( !call ) { pthread_mutex_unlock(&session->mutex); LOGGER_ERROR("Cannot handle more calls"); return -1; } - *call_index = _call->call_idx; + *call_index = call->call_idx; - t_randomstr ( _call->id, CALL_ID_LEN ); + t_randomstr ( call->id, sizeof(call->id) ); - add_peer(_call, friend_id ); + add_peer ( call, friend_id ); - _call->type_local = call_type; + call->type_local = call_type; - MSIMessage *_msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) ); + MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); - /* Do whatever with message */ - if ( call_type == type_audio ) { - msi_msg_set_calltype ( _msg_invite, ( const uint8_t *) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); - } else { - msi_msg_set_calltype ( _msg_invite, ( const uint8_t *) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); - } + msi_msg_set_calltype(msg_invite, call_type); + send_message ( session, call, msg_invite, friend_id ); + free( msg_invite ); - send_message ( session, _call, _msg_invite, friend_id ); - free_message ( _msg_invite ); + call->state = call_inviting; - _call->state = call_inviting; - - _call->request_timer_id = timer_alloc ( session->timer_handler, handle_timeout, _call, m_deftout ); + call->request_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, m_deftout ); LOGGER_DEBUG("Invite sent"); @@ -1755,26 +1559,25 @@ int msi_hangup ( MSISession *session, int32_t call_index ) return -1; } - if ( !session->calls[call_index] || session->calls[call_index]->state != call_active ) { + if ( session->calls[call_index]->state != call_active ) { LOGGER_ERROR("No call with such index or call is not active!"); pthread_mutex_unlock(&session->mutex); return -1; } - MSIMessage *_msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); + MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); /* hangup for each peer */ - int _it = 0; - - for ( ; _it < session->calls[call_index]->peer_count; _it ++ ) - send_message ( session, session->calls[call_index], _msg_end, session->calls[call_index]->peers[_it] ); + int it = 0; + for ( ; it < session->calls[call_index]->peer_count; it ++ ) + send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] ); session->calls[call_index]->state = call_hanged_up; - free_message ( _msg_end ); + free ( msg_end ); session->calls[call_index]->request_timer_id = - timer_alloc ( session->timer_handler, handle_timeout, session->calls[call_index], m_deftout ); + timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); pthread_mutex_unlock(&session->mutex); return 0; @@ -1800,24 +1603,16 @@ int msi_answer ( MSISession *session, int32_t call_index, MSICallType call_type return -1; } - MSIMessage *_msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) ); + MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); session->calls[call_index]->type_local = call_type; - if ( call_type == type_audio ) { - msi_msg_set_calltype - ( _msg_starting, ( const uint8_t *) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); - } else { - msi_msg_set_calltype - ( _msg_starting, ( const uint8_t *) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); - } - - send_message ( session, session->calls[call_index], _msg_starting, - session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); - free_message ( _msg_starting ); + msi_msg_set_calltype(msg_starting, call_type); + send_message ( session, session->calls[call_index], msg_starting, 0 ); + free ( msg_starting ); session->calls[call_index]->state = call_active; - + pthread_mutex_unlock(&session->mutex); return 0; } @@ -1834,7 +1629,7 @@ int msi_answer ( MSISession *session, int32_t call_index, MSICallType call_type int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) { pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Canceling call: %u; reason:", session, call_index, reason ? reason : "Unknown"); + LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { LOGGER_ERROR("Invalid call index!"); @@ -1842,15 +1637,19 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c return -1; } - MSIMessage *_msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) ); + MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); - if ( reason ) msi_msg_set_reason(_msg_cancel, (const uint8_t *)reason, strlen(reason)); + if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { + MSIReasonStrType reason_cast = {0}; + memcpy(reason_cast, reason, strlen(reason)); + msi_msg_set_reason(msg_cancel, reason_cast); + } - send_message ( session, session->calls[call_index], _msg_cancel, peer ); - free_message ( _msg_cancel ); + send_message ( session, session->calls[call_index], msg_cancel, peer ); + free ( msg_cancel ); /*session->calls[call_index]->state = call_hanged_up; - session->calls[call_index]->request_timer_id = timer_alloc ( handle_timeout, session->calls[call_index], m_deftout );*/ + session->calls[call_index]->request_timer_id = timer_alloc ( handle_timeout, session, call_index, m_deftout );*/ terminate_call ( session, session->calls[call_index] ); pthread_mutex_unlock(&session->mutex); @@ -1865,7 +1664,7 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c * @param call_id To which call is this action handled. * @return int */ -int msi_reject ( MSISession *session, int32_t call_index, const uint8_t *reason ) +int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) { pthread_mutex_lock(&session->mutex); LOGGER_DEBUG("Session: %p Rejecting call: %u; reason:", session, call_index, reason ? (char *)reason : "Unknown"); @@ -1876,24 +1675,78 @@ int msi_reject ( MSISession *session, int32_t call_index, const uint8_t *reason return -1; } - MSIMessage *_msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) ); + MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject ); - if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char *)reason) + 1); + if ( reason ) { + MSIReasonStrType reason_cast = {0}; + memcpy(reason_cast, reason, strlen(reason)); + msi_msg_set_reason(msg_reject, reason_cast); + } - send_message ( session, session->calls[call_index], _msg_reject, + send_message ( session, session->calls[call_index], msg_reject, session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); - free_message ( _msg_reject ); + free ( msg_reject ); session->calls[call_index]->state = call_hanged_up; - session->calls[call_index]->request_timer_id = - timer_alloc ( session->timer_handler, handle_timeout, session->calls[call_index], m_deftout ); + timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); pthread_mutex_unlock(&session->mutex); return 0; } +/** + * @brief Send invite request to friend_id. + * + * @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_type(MSISession* session, int32_t call_index, MSICallType call_type) +{ + pthread_mutex_lock(&session->mutex); + + LOGGER_DEBUG("Changing media on call: %d", call_index); + + if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { + LOGGER_ERROR("Invalid call index!"); + pthread_mutex_unlock(&session->mutex); + return -1; + } + + MSICall *call = session->calls[call_index]; + if ( call->state != call_active ) { + LOGGER_ERROR("Call is not active!"); + pthread_mutex_unlock(&session->mutex); + return -1; + } + + if ( call->type_local == call_type ) { + LOGGER_ERROR("Call is already set to the requested type!"); + pthread_mutex_unlock(&session->mutex); + return -1; + } + + call->type_local = call_type; + + MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); + + msi_msg_set_calltype ( msg_invite, call_type ); + send_message ( session, call, msg_invite, call->peers[0] ); + free ( msg_invite ); + + LOGGER_DEBUG("Request for media change sent"); + + pthread_mutex_unlock(&session->mutex); + + return 0; +} + + /** * @brief Terminate the current call. * @@ -1917,4 +1770,4 @@ int msi_stopcall ( MSISession *session, int32_t call_index ) pthread_mutex_unlock(&session->mutex); return 0; -} +} \ No newline at end of file diff --git a/toxav/msi.h b/toxav/msi.h index 0020df4c..4f73e58b 100644 --- a/toxav/msi.h +++ b/toxav/msi.h @@ -1,4 +1,4 @@ -/** toxmsi.h +/** msi.h * * Copyright (C) 2013 Tox project All Rights Reserved. * @@ -27,11 +27,9 @@ #include "../toxcore/Messenger.h" -/* define size for call_id */ -#define CALL_ID_LEN 12 - - -typedef void ( *MSICallback ) ( int32_t, void *arg ); +typedef uint8_t MSICallIDType[12]; +typedef uint8_t MSIReasonStrType[255]; +typedef void ( *MSICallbackType ) ( void* agent, int32_t call_idx, void *arg ); /** @@ -57,6 +55,36 @@ typedef enum { +/** + * @brief Callbacks ids that handle the states + */ +typedef enum { + /* Requests */ + MSI_OnInvite, + MSI_OnStart, + MSI_OnCancel, + MSI_OnReject, + MSI_OnEnd, + + /* Responses */ + MSI_OnRinging, + MSI_OnStarting, + MSI_OnEnding, + + /* Protocol */ + MSI_OnRequestTimeout, + MSI_OnPeerTimeout, + MSI_OnMediaChange +} MSICallbackID; + +/** + * @brief Callbacks container + */ +typedef struct _MSICallbackCont { + MSICallbackType function; + void *data; +} MSICallbackCont; + /** * @brief The call struct. * @@ -69,7 +97,7 @@ typedef struct _MSICall { /* Call info structure */ MSICallType type_local; /* Type of payload user is ending */ MSICallType *type_peer; /* Type of payload others are sending */ - uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */ + MSICallIDType id; /* Random value identifying the call */ int ringing_tout_ms; /* Ringing timeout in ms */ @@ -92,56 +120,30 @@ typedef struct _MSICall { /* Call info structure */ typedef struct _MSISession { /* Call handlers */ - struct _MSICall **calls; - int32_t max_calls; + MSICall **calls; + int32_t max_calls; - int last_error_id; /* Determine the last error */ - const uint8_t *last_error_str; + void *agent_handler; + Messenger *messenger_handle; - void *agent_handler; /* Pointer to an object that is handling msi */ - Messenger *messenger_handle; - - uint32_t frequ; - uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ + uint32_t frequ; + uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ pthread_mutex_t mutex; - void *timer_handler; + void *timer_handler; + MSICallbackCont callbacks[11]; /* Callbacks used by this session */ } MSISession; - -/** - * @brief Callbacks ids that handle the states - */ -typedef enum { - /* Requests */ - MSI_OnInvite, - MSI_OnStart, - MSI_OnCancel, - MSI_OnReject, - MSI_OnEnd, - - /* Responses */ - MSI_OnRinging, - MSI_OnStarting, - MSI_OnEnding, - - /* Protocol */ - MSI_OnError, - MSI_OnRequestTimeout, - MSI_OnPeerTimeout - -} MSICallbackID; - - /** * @brief Callback setter. * + * @param session The container. * @param callback The callback. * @param id The id. * @return void */ -void msi_register_callback(MSICallback callback, MSICallbackID id, void *userdata); +void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); /** @@ -220,7 +222,20 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c * @param reason Set optional reason header. Pass NULL if none. * @return int */ -int msi_reject ( MSISession *session, int32_t call_index, const uint8_t *reason ); +int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); + + +/** + * @brief Send invite request to friend_id. + * + * @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_type ( MSISession *session, int32_t call_index, MSICallType call_type ); /** diff --git a/toxav/rtp.c b/toxav/rtp.c index 521e4b22..8b94bda7 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -1,4 +1,4 @@ -/** toxrtp.c +/** rtp.c * * Copyright (C) 2013 Tox project All Rights Reserved. * @@ -372,11 +372,6 @@ int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) return -1; } - if ( _session->queue_limit <= _session->queue_size ) { - LOGGER_WARNING("Queue limit reached!"); - return -1; - } - _msg = msg_parse ( data + 1, length - 1 ); if ( !_msg ) { @@ -390,18 +385,7 @@ int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) _session->timestamp = _msg->header->timestamp; } - pthread_mutex_lock(&_session->mutex); - - if ( _session->last_msg ) { - _session->last_msg->next = _msg; - _session->last_msg = _msg; - } else { - _session->last_msg = _session->oldest_msg = _msg; - } - - _session->queue_size++; - - pthread_mutex_unlock(&_session->mutex); + toxav_handle_packet(_session, _msg); return 0; } @@ -467,105 +451,6 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t } - -/** - * @brief Release all messages held by session. - * - * @param session The session. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_release_session_recv ( RTPSession *session ) -{ - if ( !session ) { - LOGGER_WARNING("No session!"); - return -1; - } - - RTPMessage *_tmp, * _it; - - pthread_mutex_lock(&session->mutex); - - for ( _it = session->oldest_msg; _it; _it = _tmp ) { - _tmp = _it->next; - rtp_free_msg( session, _it); - } - - session->last_msg = session->oldest_msg = NULL; - session->queue_size = 0; - - pthread_mutex_unlock(&session->mutex); - - return 0; -} - - -/** - * @brief Call this to change queue limit - * - * @param session The session - * @param limit new limit - * @return void - */ -void rtp_queue_adjust_limit(RTPSession *session, uint64_t limit) -{ - pthread_mutex_lock(&session->mutex); - - RTPMessage *_tmp, * _it = session->oldest_msg; - - for ( ; session->queue_size > limit; _it = _tmp ) { - _tmp = _it->next; - rtp_free_msg( session, _it); - session->queue_size --; - } - - session->oldest_msg = _it; - session->queue_limit = limit; - - pthread_mutex_unlock(&session->mutex); -} - - -/** - * @brief Gets oldest message in the list. - * - * @param session Where the list is. - * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it. - * @retval NULL No messages in the list, or no list. - */ -RTPMessage *rtp_recv_msg ( RTPSession *session ) -{ - if ( !session ) { - LOGGER_WARNING("No session!"); - return NULL; - } - - pthread_mutex_lock(&session->mutex); - - if ( session->queue_size == 0 ) { - pthread_mutex_unlock(&session->mutex); - return NULL; - } - - - RTPMessage *_retu = session->oldest_msg; - - /*if (_retu)*/ - session->oldest_msg = _retu->next; - - if ( !session->oldest_msg ) - session->last_msg = NULL; - - session->queue_size --; - - pthread_mutex_unlock(&session->mutex); - - - return _retu; -} - - /** * @brief Sends data to _RTPSession::dest * @@ -627,7 +512,6 @@ void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) free ( msg ); } - /** * @brief Must be called before calling any other rtp function. It's used * to initialize RTP control session. @@ -682,11 +566,6 @@ RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int frien /* Also set payload type as prefix */ _retu->prefix = payload_type; - _retu->oldest_msg = _retu->last_msg = NULL; - _retu->queue_limit = 100; /* Default */ - _retu->queue_size = 0; - - pthread_mutex_init(&_retu->mutex, NULL); /* * */ @@ -705,24 +584,16 @@ RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int frien */ void rtp_terminate_session ( RTPSession *session, Messenger *messenger ) { - if ( !session ) return; + if ( !session ) return; custom_lossy_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL); - rtp_release_session_recv(session); - - pthread_mutex_lock(&session->mutex); - free ( session->ext_header ); free ( session->csrc ); - pthread_mutex_unlock(&session->mutex); - - pthread_mutex_destroy(&session->mutex); - LOGGER_DEBUG("Terminated RTP session: %p", session); - + /* And finally free session */ free ( session ); - -} \ No newline at end of file + +} diff --git a/toxav/rtp.h b/toxav/rtp.h index 727839c6..d57c5ef7 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -1,4 +1,4 @@ -/** toxrtp.h +/** rtp.h * * Copyright (C) 2013 Tox project All Rights Reserved. * @@ -105,17 +105,12 @@ typedef struct _RTPSession { */ RTPExtHeader *ext_header; - RTPMessage *oldest_msg; - RTPMessage *last_msg; /* tail */ - - uint64_t queue_limit;/* Default 100; modify per thy liking */ - uint64_t queue_size; /* currently holding << messages */ - /* Msg prefix for core to know when recving */ uint8_t prefix; - pthread_mutex_t mutex; int dest; + int32_t call_index; + struct _ToxAv *av; } RTPSession; @@ -172,7 +167,6 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat */ 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. diff --git a/toxav/toxav.c b/toxav/toxav.c index 19fcd854..003c2116 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -76,6 +76,10 @@ struct _ToxAv { Messenger *messenger; MSISession *msi_session; /** Main msi session */ CallSpecific *calls; /** Per-call params */ + + void (*audio_callback)(ToxAv *, int32_t, int16_t *, int); + void (*video_callback)(ToxAv *, int32_t, vpx_image_t *); + uint32_t max_calls; }; @@ -115,10 +119,8 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls) } av->messenger = (Messenger *)messenger; - av->msi_session = msi_init_session(av->messenger, max_calls); av->msi_session->agent_handler = av; - av->calls = calloc(sizeof(CallSpecific), max_calls); av->max_calls = max_calls; @@ -133,8 +135,6 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls) */ void toxav_kill ( ToxAv *av ) { - msi_terminate_session(av->msi_session); - int i = 0; for (; i < av->max_calls; i ++) { @@ -152,6 +152,8 @@ void toxav_kill ( ToxAv *av ) if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs); } + msi_terminate_session(av->msi_session); + free(av->calls); free(av); } @@ -159,13 +161,36 @@ void toxav_kill ( ToxAv *av ) /** * @brief Register callback for call state. * + * @param av Handler. * @param callback The callback * @param id One of the ToxAvCallbackID values * @return void */ -void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id, void *userdata ) +void toxav_register_callstate_callback ( ToxAv* av, ToxAVCallback callback, ToxAvCallbackID id, void* userdata ) { - msi_register_callback((MSICallback)callback, (MSICallbackID) id, userdata); + msi_register_callback(av->msi_session, (MSICallbackType)callback, (MSICallbackID) id, userdata); +} + +/** + * @brief Register callback for recieving audio data + * + * @param callback The callback + * @return void + */ +void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int)) +{ + av->audio_callback = callback; +} + +/** + * @brief Register callback for recieving video data + * + * @param callback The callback + * @return void + */ +void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *)) +{ + av->video_callback = callback; } /** @@ -272,6 +297,24 @@ int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reaso return msi_cancel(av->msi_session, call_index, peer_id, reason); } +/** + * @brief Notify peer that we are changing call type + * + * @param av Handler. + * @return int + * @param call_type Change to... + * @retval 0 Success. + * @retval ToxAvError On error. + */ +int toxav_change_type(ToxAv* av, int32_t call_index, ToxAvCallType call_type) +{ + if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { + return ErrorNoCall; + } + + return msi_change_type(av->msi_session, call_index, call_type); +} + /** * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. * @@ -300,7 +343,7 @@ int toxav_stop_call ( ToxAv *av, int32_t call_index ) int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video ) { if ( !av->msi_session || cii(call_index, av->msi_session) || - !av->msi_session->calls[call_index] || av->calls[call_index].call_active) { + !av->msi_session->calls[call_index] || av->calls[call_index].call_active) { LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); return ErrorInternal; } @@ -316,17 +359,21 @@ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettin return ErrorInternal; } + call->crtps[audio_index]->call_index = call_index; + call->crtps[audio_index]->av = av; if ( support_video ) { call->crtps[video_index] = rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); - if ( !call->crtps[video_index] ) { LOGGER_ERROR("Error while starting video RTP session!\n"); goto error; } + call->crtps[video_index]->call_index = call_index; + call->crtps[video_index]->av = av; + call->frame_limit = 0; call->frame_id = 0; call->frame_outid = 0; @@ -350,17 +397,17 @@ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettin codec_settings->audio_sample_rate, codec_settings->audio_channels, codec_settings->audio_VAD_tolerance, - codec_settings->video_width, - codec_settings->video_height, + codec_settings->max_video_width, + codec_settings->max_video_height, codec_settings->video_bitrate) )) { - - if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; - + + if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; + call->call_active = 1; - + return ErrorNone; } - + error: rtp_terminate_session(call->crtps[audio_index], av->messenger); rtp_terminate_session(call->crtps[video_index], av->messenger); @@ -387,22 +434,27 @@ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) } CallSpecific *call = &av->calls[call_index]; - + pthread_mutex_lock(&call->mutex); - - if (!call->call_active){ + + if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } - + + call->call_active = 0; - rtp_terminate_session(call->crtps[audio_index], av->messenger); call->crtps[audio_index] = NULL; - rtp_terminate_session(call->crtps[video_index], av->messenger); call->crtps[video_index] = NULL; - terminate_queue(call->j_buf); call->j_buf = NULL; - codec_terminate_session(call->cs); call->cs = NULL; - + rtp_terminate_session(call->crtps[audio_index], av->messenger); + call->crtps[audio_index] = NULL; + rtp_terminate_session(call->crtps[video_index], av->messenger); + call->crtps[video_index] = NULL; + terminate_queue(call->j_buf); + call->j_buf = NULL; + codec_terminate_session(call->cs); + call->cs = NULL; + pthread_mutex_unlock(&call->mutex); pthread_mutex_destroy(&call->mutex); @@ -424,9 +476,10 @@ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, unsigned int length ) { - CallSpecific* call = &av->calls[call_index]; + CallSpecific *call = &av->calls[call_index]; + if (call->crtps[type - TypeAudio]) { - + if (type == TypeAudio) { return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, payload, length); } else { @@ -447,10 +500,10 @@ inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallTy for (i = 0; i < numparts; i++) { memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, VIDEOFRAME_PIECE_SIZE); payload += VIDEOFRAME_PIECE_SIZE; - - if (rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, + + if (rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, load, VIDEOFRAME_HEADER_SIZE + VIDEOFRAME_PIECE_SIZE) != 0) { - + return ErrorInternal; } @@ -460,7 +513,7 @@ inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallTy /* remainder = length % VIDEOFRAME_PIECE_SIZE, VIDEOFRAME_PIECE_SIZE if = 0 */ length = ((length - 1) % VIDEOFRAME_PIECE_SIZE) + 1; memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, length); - + return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, load, VIDEOFRAME_HEADER_SIZE + length); } } else { @@ -468,141 +521,6 @@ inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallTy } } -/** - * @brief Receive RTP payload. - * - * @param av Handler. - * @param type Type of the payload. - * @param dest Storage. - * @return int - * @retval ToxAvError On Error. - * @retval >=0 Size of received payload. - */ -inline__ int toxav_recv_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, uint8_t *dest ) -{ - if ( !dest ) return ErrorInternal; - - CallSpecific *call = &av->calls[call_index]; - - if ( !call->crtps[type - TypeAudio] ) return ErrorNoRtpSession; - - RTPMessage *message; - - if ( type == TypeAudio ) { - - do { - message = rtp_recv_msg(call->crtps[audio_index]); - - if (message) { - /* push the packet into the queue */ - queue(call->j_buf, message); - } - } while (message); - - int success = 0; - message = dequeue(call->j_buf, &success); - - if ( success == 2) return ErrorAudioPacketLost; - } else { - message = rtp_recv_msg(call->crtps[video_index]); - } - - if ( message ) { - memcpy(dest, message->data, message->length); - - int length = message->length; - - rtp_free_msg(NULL, message); - - return length; - } - - return 0; -} - -/** - * @brief Receive decoded video packet. - * - * @param av Handler. - * @param output Storage. - * @return int - * @retval 0 Success. - * @retval ToxAvError On Error. - */ -inline__ int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output) -{ - if ( !output ) return ErrorInternal; - - if (cii(call_index, av->msi_session)) { - LOGGER_WARNING("Invalid call index: %d", call_index); - return ErrorNoCall; - } - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - if (!call->call_active){ - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - - uint8_t packet [RTP_PAYLOAD_SIZE]; - int recved_size; - - while ((recved_size = toxav_recv_rtp_payload(av, call_index, TypeVideo, packet)) > 0) { - if (recved_size < VIDEOFRAME_HEADER_SIZE) { - continue; - } - - uint8_t i = packet[0] - call->frame_id; - - if (i == 0) { - /* piece of current frame */ - } else if (i > 0 && i < 128) { - /* recieved a piece of a frame ahead, flush current frame and start reading this new frame */ - int rc = vpx_codec_decode(&call->cs->v_decoder, call->frame_buf, call->frame_limit, NULL, 0); - call->frame_id = packet[0]; - memset(call->frame_buf, 0, call->frame_limit); - call->frame_limit = 0; - - if (rc != VPX_CODEC_OK) { - LOGGER_ERROR("Error decoding video: %u %s\n", i, vpx_codec_err_to_string(rc)); - } - } else { - /* old packet, dont read */ - LOGGER_DEBUG("Old packet: %u\n", i); - continue; - } - - if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / - VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure - /* packet out of buffer range */ - continue; - } - - LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); - memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, - recved_size - VIDEOFRAME_HEADER_SIZE); - uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; - - if (limit > call->frame_limit) { - call->frame_limit = limit; - LOGGER_DEBUG("Limit: %u\n", call->frame_limit); - } - } - - vpx_codec_iter_t iter = NULL; - vpx_image_t *img; - img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); - - *output = img; - - pthread_mutex_unlock(&call->mutex); - return ErrorNone; -} - /** * @brief Encode and send video packet. * @@ -619,19 +537,19 @@ inline__ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *fr return ErrorNoCall; } - CallSpecific* call = &av->calls[call_index]; + CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); - - - if (!call->call_active){ + + + if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } - + int rc = toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); pthread_mutex_unlock(&call->mutex); - + return rc; } @@ -656,14 +574,17 @@ inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *d CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); - - if (!call->call_active){ + + if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } - - reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h); + + if (reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { + pthread_mutex_unlock(&call->mutex); + return ErrorInternal; + } int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); @@ -681,7 +602,7 @@ inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *d while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - if ( copied + pkt->data.frame.sz > dest_max ) { + if ( copied + pkt->data.frame.sz > dest_max ) { pthread_mutex_unlock(&call->mutex); return ErrorPacketTooLarge; } @@ -695,67 +616,6 @@ inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *d return copied; } -/** - * @brief Receive decoded audio frame. - * - * @param av Handler. - * @param frame_size The size of dest in frames/samples (one frame/sample is 16 bits or 2 bytes - * and corresponds to one sample of audio.) - * @param dest Destination of the raw audio (16 bit signed pcm with AUDIO_CHANNELS channels). - * Make sure it has enough space for frame_size frames/samples. - * @return int - * @retval >=0 Size of received data in frames/samples. - * @retval ToxAvError On error. - */ -inline__ int toxav_recv_audio ( ToxAv *av, int32_t call_index, int frame_size, int16_t *dest ) -{ - if ( !dest ) return ErrorInternal; - - if (cii(call_index, av->msi_session)) { - LOGGER_WARNING("Invalid call index: %d", call_index); - return ErrorNoCall; - } - - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - - if (!call->call_active){ - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - uint8_t packet [RTP_PAYLOAD_SIZE]; - - int recved_size = toxav_recv_rtp_payload(av, call_index, TypeAudio, packet); - - if ( recved_size == ErrorAudioPacketLost ) { - int dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1); - - pthread_mutex_unlock(&call->mutex); - - if ( dec_size < 0 ) { - LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); - return ErrorInternal; - } else return dec_size; - - } else if ( recved_size ) { - int dec_size = opus_decode(call->cs->audio_decoder, packet, recved_size, dest, frame_size, 0); - - pthread_mutex_unlock(&call->mutex); - - if ( dec_size < 0 ) { - LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); - return ErrorInternal; - } else return dec_size; - } else { - pthread_mutex_unlock(&call->mutex); - return 0; /* Nothing received */ - } -} - /** * @brief Send audio frame. * @@ -774,19 +634,19 @@ inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *fr return ErrorNoCall; } - CallSpecific* call = &av->calls[call_index]; + CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); - - - if (!call->call_active){ + + + if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } - + int rc = toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size); pthread_mutex_unlock(&call->mutex); - + return rc; } @@ -810,19 +670,18 @@ inline__ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t return ErrorNoCall; } - CallSpecific* call = &av->calls[call_index]; + CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); - - - if (!call->call_active){ + + + if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } - - int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); - pthread_mutex_unlock(&call->mutex); + int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); + pthread_mutex_unlock(&call->mutex); if (rc < 0) { LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); @@ -898,42 +757,6 @@ inline__ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCa /* 0 is error here */ } -/** - * @brief Set queue limit - * - * @param av Handler - * @param call_index index - * @param limit the limit - * @return void - */ -inline__ int toxav_set_audio_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit) -{ - if ( av->calls[call_index].crtps[audio_index] ) - rtp_queue_adjust_limit(av->calls[call_index].crtps[audio_index], limit); - else - return ErrorNoRtpSession; - - return ErrorNone; -} - -/** - * @brief Set queue limit - * - * @param av Handler - * @param call_index index - * @param limit the limit - * @return void - */ -inline__ int toxav_set_video_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit) -{ - if ( av->calls[call_index].crtps[video_index] ) - rtp_queue_adjust_limit(av->calls[call_index].crtps[video_index], limit); - else - return ErrorNoRtpSession; - - return ErrorNone; -} - inline__ Tox *toxav_get_tox(ToxAv *av) { return (Tox *)av->messenger; @@ -945,3 +768,94 @@ int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t fra return energy_VAD(av->calls[call_index].cs, PCM, frame_size, ref_energy); } + +void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg) +{ + ToxAv *av = _session->av; + int32_t call_index = _session->call_index; + CallSpecific *call = &av->calls[call_index]; + + if (!call->call_active) return; + + if (_session->payload_type == type_audio % 128) { + queue(call->j_buf, _msg); + + int success = 0, dec_size; + int frame_size = 960; + int16_t dest[frame_size]; + + while ((_msg = dequeue(call->j_buf, &success)) || success == 2) { + if (success == 2) { + dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1); + } else { + dec_size = opus_decode(call->cs->audio_decoder, _msg->data, _msg->length, dest, frame_size, 0); + rtp_free_msg(NULL, _msg); + } + + if (dec_size < 0) { + LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); + continue; + } + + if ( av->audio_callback ) + av->audio_callback(av, call_index, dest, frame_size); + else + LOGGER_WARNING("Audio packet dropped due to missing callback!"); + } + } else { + uint8_t *packet = _msg->data; + int recved_size = _msg->length; + + if (recved_size < VIDEOFRAME_HEADER_SIZE) { + goto end; + } + + uint8_t i = packet[0] - call->frame_id; + + if (i == 0) { + /* piece of current frame */ + } else if (i > 0 && i < 128) { + /* recieved a piece of a frame ahead, flush current frame and start reading this new frame */ + int rc = vpx_codec_decode(&call->cs->v_decoder, call->frame_buf, call->frame_limit, NULL, 0); + call->frame_id = packet[0]; + memset(call->frame_buf, 0, call->frame_limit); + call->frame_limit = 0; + + if (rc != VPX_CODEC_OK) { + LOGGER_ERROR("Error decoding video: %u %s\n", i, vpx_codec_err_to_string(rc)); + } + } else { + /* old packet, dont read */ + LOGGER_DEBUG("Old packet: %u\n", i); + goto end; + } + + if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / + VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure + /* packet out of buffer range */ + goto end; + } + + LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); + memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, + recved_size - VIDEOFRAME_HEADER_SIZE); + uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; + + if (limit > call->frame_limit) { + call->frame_limit = limit; + LOGGER_DEBUG("Limit: %u\n", call->frame_limit); + } + +end:; + vpx_codec_iter_t iter = NULL; + vpx_image_t *img; + img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); + + if (img && av->video_callback) { + av->video_callback(av, call_index, img); + } else + LOGGER_WARNING("Video packet dropped due to missing callback or no image!"); + + rtp_free_msg(NULL, _msg); + } +} diff --git a/toxav/toxav.h b/toxav/toxav.h index 2e71c4da..03c3f5e8 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -31,7 +31,7 @@ extern "C" { /* vpx_image_t */ #include -typedef void ( *ToxAVCallback ) ( int32_t, void *arg ); +typedef void ( *ToxAVCallback ) ( void* agent, int32_t call_idx, void *arg ); typedef struct _ToxAv ToxAv; #ifndef __TOX_DEFINED__ @@ -59,9 +59,9 @@ typedef enum { av_OnEnding, /* Protocol */ - av_OnError, av_OnRequestTimeout, - av_OnPeerTimeout + av_OnPeerTimeout, + av_OnMediaChange } ToxAvCallbackID; @@ -120,8 +120,8 @@ typedef enum { */ typedef struct _ToxAvCodecSettings { uint32_t video_bitrate; /* In kbits/s */ - uint16_t video_width; /* In px */ - uint16_t video_height; /* In px */ + uint16_t max_video_width; /* In px */ + uint16_t max_video_height; /* In px */ uint32_t audio_bitrate; /* In bits/s */ uint16_t audio_frame_duration; /* In ms */ @@ -158,11 +158,30 @@ void toxav_kill(ToxAv *av); /** * @brief Register callback for call state. * + * @param av Handler. * @param callback The callback * @param id One of the ToxAvCallbackID values * @return void */ -void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id, void *userdata); +void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata); + +/** + * @brief Register callback for recieving audio data + * + * @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)); + +/** + * @brief Register callback for recieving video data + * + * @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 *)); /** * @brief Call user. Use its friend_id. @@ -221,6 +240,16 @@ int toxav_reject(ToxAv *av, int32_t call_index, 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 type + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ +int toxav_change_type(ToxAv *av, int32_t call_index, ToxAvCallType call_type); + /** * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. * @@ -252,31 +281,6 @@ int toxav_prepare_transmission(ToxAv *av, int32_t call_index, ToxAvCodecSettings */ int toxav_kill_transmission(ToxAv *av, int32_t call_index); -/** - * @brief Receive decoded video packet. - * - * @param av Handler. - * @param output Storage. - * @return int - * @retval 0 Success. - * @retval ToxAvError On Error. - */ -int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output); - -/** - * @brief Receive decoded audio frame. - * - * @param av Handler. - * @param frame_size The size of dest in frames/samples (one frame/sample is 16 bits or 2 bytes - * and corresponds to one sample of audio.) - * @param dest Destination of the raw audio (16 bit signed pcm with AUDIO_CHANNELS channels). - * Make sure it has enough space for frame_size frames/samples. - * @return int - * @retval >=0 Size of received data in frames/samples. - * @retval ToxAvError On error. - */ -int toxav_recv_audio( ToxAv *av, int32_t call_index, int frame_size, int16_t *dest ); - /** * @brief Encode and send video packet. * @@ -399,4 +403,4 @@ int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t f } #endif -#endif /* __TOXAV */ \ No newline at end of file +#endif /* __TOXAV */