mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Merge branch 'master' of https://github.com/mannol/toxcore
This commit is contained in:
commit
279c33c01a
|
@ -11,6 +11,8 @@
|
|||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/crypto_core.h"
|
||||
|
@ -33,7 +35,7 @@ typedef enum _CallStatus {
|
|||
Ringing,
|
||||
Ended,
|
||||
Rejected,
|
||||
Cancel,
|
||||
Canceled,
|
||||
TimedOut
|
||||
|
||||
} CallStatus;
|
||||
|
@ -66,36 +68,23 @@ void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
|
|||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
if (cast->Alice.av == av) {
|
||||
// ...
|
||||
} else if (cast->Bob.av == av) {
|
||||
/* Bob always receives invite */
|
||||
cast->Bob.status = Ringing;
|
||||
cast->Bob.call_index = call_index;
|
||||
}
|
||||
}
|
||||
void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
if (cast->Alice.av == av) {
|
||||
/* Alice always sends invite */
|
||||
cast->Alice.status = Ringing;
|
||||
}
|
||||
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(av, call_index, av_jbufdc, av_VADd, 1);
|
||||
}
|
||||
void callback_recv_ending ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
if ( cast->Alice.status == Rejected) {
|
||||
printf ( "Call ended for Bob!\n" );
|
||||
cast->Bob.status = Ended;
|
||||
} else {
|
||||
printf ( "Call ended for Alice!\n" );
|
||||
cast->Alice.status = Ended;
|
||||
} else if (cast->Bob.av == av) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,17 +93,26 @@ void callback_call_started ( void *av, int32_t call_index, void *_arg )
|
|||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
/* Alice always sends invite */
|
||||
if (cast->Alice.av == av) {
|
||||
printf("Call started on Alices side...\n");
|
||||
cast->Alice.status = InCall;
|
||||
toxav_prepare_transmission(av, call_index, 1);
|
||||
} else if (cast->Bob.av == av) {
|
||||
printf("Call started on Bob side...\n");
|
||||
cast->Bob.status = InCall;
|
||||
toxav_prepare_transmission(av, call_index, av_jbufdc, av_VADd, 1);
|
||||
toxav_prepare_transmission(av, call_index, 1);
|
||||
}
|
||||
}
|
||||
void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
if (cast->Alice.av == av) {
|
||||
// ...
|
||||
} else if (cast->Bob.av == av) {
|
||||
printf ( "Call Canceled for Bob!\n" );
|
||||
cast->Bob.status = Cancel;
|
||||
cast->Bob.status = Canceled;
|
||||
}
|
||||
}
|
||||
void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
|
@ -122,23 +120,58 @@ void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
|
|||
|
||||
printf ( "Call rejected by Bob!\n"
|
||||
"Call ended for Alice!\n" );
|
||||
|
||||
/* If Bob rejects, call is ended for alice and she sends ending */
|
||||
if (cast->Alice.av == av) {
|
||||
cast->Alice.status = Rejected;
|
||||
} else if (cast->Bob.av == av) {
|
||||
//... ignor
|
||||
}
|
||||
}
|
||||
void callback_call_ended ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
if (cast->Alice.av == av) {
|
||||
printf ( "Call ended for Alice!\n" );
|
||||
cast->Alice.status = Ended;
|
||||
} else if (cast->Bob.av == av) {
|
||||
printf ( "Call ended for Bob!\n" );
|
||||
cast->Bob.status = Ended;
|
||||
}
|
||||
}
|
||||
|
||||
void callback_call_type_change ( void *av, int32_t call_index, void *_arg )
|
||||
void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
ToxAvCSettings csettings;
|
||||
toxav_get_peer_csettings(av, call_index, 0, &csettings);
|
||||
|
||||
printf("New settings: \n"
|
||||
printf("Peer changing settings to: \n"
|
||||
"Type: %u \n"
|
||||
"Video bitrate: %u \n"
|
||||
"Video height: %u \n"
|
||||
"Video width: %u \n"
|
||||
"Audio bitrate: %u \n"
|
||||
"Audio framedur: %u \n"
|
||||
"Audio sample rate: %u \n"
|
||||
"Audio channels: %u \n",
|
||||
csettings.call_type,
|
||||
csettings.video_bitrate,
|
||||
csettings.max_video_height,
|
||||
csettings.max_video_width,
|
||||
csettings.audio_bitrate,
|
||||
csettings.audio_frame_duration,
|
||||
csettings.audio_sample_rate,
|
||||
csettings.audio_channels
|
||||
);
|
||||
}
|
||||
|
||||
void callback_self_cs_change ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
ToxAvCSettings csettings;
|
||||
toxav_get_peer_csettings(av, call_index, 0, &csettings);
|
||||
|
||||
printf("Changed settings to: \n"
|
||||
"Type: %u \n"
|
||||
"Video bitrate: %u \n"
|
||||
"Video height: %u \n"
|
||||
|
@ -162,16 +195,19 @@ void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
|
|||
{
|
||||
Status *cast = _arg;
|
||||
printf("Call timed-out!\n");
|
||||
|
||||
if (cast->Alice.av == av) {
|
||||
cast->Alice.status = TimedOut;
|
||||
} else if (cast->Bob.av == av) {
|
||||
cast->Bob.status = TimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length, void *userdata)
|
||||
{
|
||||
}
|
||||
void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
|
||||
{}
|
||||
|
||||
static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img, void *userdata)
|
||||
{
|
||||
}
|
||||
void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
|
||||
{}
|
||||
|
||||
void register_callbacks(ToxAv *av, void *data)
|
||||
{
|
||||
|
@ -180,17 +216,12 @@ void register_callbacks(ToxAv *av, void *data)
|
|||
toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
|
||||
toxav_register_callstate_callback(av, callback_call_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, NULL);
|
||||
toxav_register_video_recv_callback(av, callback_video, NULL);
|
||||
toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data);
|
||||
toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data);
|
||||
toxav_register_audio_callback(callback_audio, NULL);
|
||||
toxav_register_video_callback(callback_video, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,6 +233,7 @@ void register_callbacks(ToxAv *av, void *data)
|
|||
#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \
|
||||
{ int step = 0, running = 1; while (running) {\
|
||||
tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); \
|
||||
toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \
|
||||
switch ( step ) {\
|
||||
case 0: /* Alice */ printf("Alice is calling...\n");\
|
||||
toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); step++; break;\
|
||||
|
@ -496,6 +528,10 @@ START_TEST(test_AV_flows)
|
|||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
|
||||
toxav_do(status_control.Alice.av);
|
||||
toxav_do(status_control.Bob.av);
|
||||
|
||||
|
||||
switch ( step ) {
|
||||
case 0: /* Alice */
|
||||
printf("Alice is calling...\n");
|
||||
|
@ -514,7 +550,7 @@ START_TEST(test_AV_flows)
|
|||
break;
|
||||
|
||||
case 2: /* Wait for Both to have status ended */
|
||||
if (status_control.Bob.status == Cancel) running = 0;
|
||||
if (status_control.Bob.status == Canceled) running = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -537,6 +573,9 @@ START_TEST(test_AV_flows)
|
|||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
|
||||
toxav_do(status_control.Alice.av);
|
||||
toxav_do(status_control.Bob.av);
|
||||
|
||||
switch ( step ) {
|
||||
case 0:
|
||||
printf("Alice is calling...\n");
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/crypto_core.h"
|
||||
|
@ -32,7 +34,7 @@ typedef enum _CallStatus {
|
|||
Ringing,
|
||||
Ended,
|
||||
Rejected,
|
||||
Cancel
|
||||
Canceled
|
||||
|
||||
} CallStatus;
|
||||
|
||||
|
@ -67,66 +69,51 @@ void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *dat
|
|||
/******************************************************************************/
|
||||
void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
/*
|
||||
Status *cast = _arg;
|
||||
|
||||
cast->calls[call_index].Callee.status = Ringing;*/
|
||||
cast->calls[call_index].Callee.status = Ringing;
|
||||
}
|
||||
void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Caller.status = Ringing;
|
||||
}
|
||||
void callback_recv_starting ( void *av, int32_t call_index, void *_arg )
|
||||
void callback_call_ended ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Caller.status = InCall;
|
||||
}
|
||||
void callback_recv_ending ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Caller.status = Ended;
|
||||
}
|
||||
|
||||
if (av == cast->calls[call_index].Caller.av)
|
||||
cast->calls[call_index].Caller.status = Ended;
|
||||
else
|
||||
cast->calls[call_index].Callee.status = Ended;
|
||||
}
|
||||
void callback_call_started ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
/*
|
||||
Status *cast = _arg;
|
||||
|
||||
cast->calls[call_index].Callee.status = InCall;*/
|
||||
if (av == cast->calls[call_index].Caller.av)
|
||||
cast->calls[call_index].Caller.status = InCall;
|
||||
else
|
||||
cast->calls[call_index].Callee.status = InCall;
|
||||
}
|
||||
void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
/*
|
||||
Status *cast = _arg;
|
||||
|
||||
cast->calls[call_index].Callee.status = Cancel;*/
|
||||
}
|
||||
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 ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
/*
|
||||
Status *cast = _arg;
|
||||
|
||||
cast->calls[call_index].Callee.status = Ended;*/
|
||||
}
|
||||
|
||||
void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
//ck_assert_msg(0, "No answer!");
|
||||
ck_assert_msg(0, "No answer!");
|
||||
}
|
||||
|
||||
static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length, void *userdata)
|
||||
{
|
||||
}
|
||||
void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
|
||||
{}
|
||||
|
||||
static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img, void *userdata)
|
||||
{
|
||||
}
|
||||
void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
|
||||
{}
|
||||
|
||||
void register_callbacks(ToxAv *av, void *data)
|
||||
{
|
||||
|
@ -135,16 +122,12 @@ void register_callbacks(ToxAv *av, void *data)
|
|||
toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
|
||||
toxav_register_callstate_callback(av, callback_call_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, NULL);
|
||||
toxav_register_video_recv_callback(av, callback_video, NULL);
|
||||
toxav_register_audio_callback(callback_audio, NULL);
|
||||
toxav_register_video_callback(callback_video, NULL);
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
|
||||
|
@ -193,14 +176,14 @@ void *in_thread_call (void *arg)
|
|||
call_print(call_idx, "Sending rtp ...");
|
||||
|
||||
c_sleep(1000); /* We have race condition here */
|
||||
toxav_prepare_transmission(this_call->Callee.av, 0, 3, 0, 1);
|
||||
toxav_prepare_transmission(this_call->Caller.av, call_idx, 3, 0, 1);
|
||||
toxav_prepare_transmission(this_call->Callee.av, 0, 1);
|
||||
toxav_prepare_transmission(this_call->Caller.av, call_idx, 1);
|
||||
|
||||
int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE,
|
||||
sample_payload, frame_size);
|
||||
|
||||
if ( payload_size < 0 ) {
|
||||
//ck_assert_msg ( 0, "Failed to encode payload" );
|
||||
ck_assert_msg ( 0, "Failed to encode payload" );
|
||||
}
|
||||
|
||||
|
||||
|
@ -253,8 +236,8 @@ void *in_thread_call (void *arg)
|
|||
|
||||
|
||||
|
||||
// START_TEST(test_AV_three_calls)
|
||||
void test_AV_three_calls()
|
||||
START_TEST(test_AV_three_calls)
|
||||
// void test_AV_three_calls()
|
||||
{
|
||||
long long unsigned int cur_time = time(NULL);
|
||||
Tox *bootstrap_node = tox_new(0);
|
||||
|
@ -266,12 +249,12 @@ void test_AV_three_calls()
|
|||
};
|
||||
|
||||
|
||||
//ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node");
|
||||
ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node");
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < 3; i ++) {
|
||||
//ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances");
|
||||
ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances");
|
||||
}
|
||||
|
||||
for ( i = 0; i < 3; i ++ ) {
|
||||
|
@ -281,7 +264,7 @@ void test_AV_three_calls()
|
|||
tox_get_address(callees[i], address);
|
||||
|
||||
int test = tox_add_friend(caller, address, (uint8_t *)"gentoo", 7);
|
||||
//ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
|
||||
ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
|
||||
}
|
||||
|
||||
uint8_t off = 1;
|
||||
|
@ -350,6 +333,13 @@ void test_AV_three_calls()
|
|||
tox_do(callees[1]);
|
||||
tox_do(callees[2]);
|
||||
|
||||
for ( i = 0; i < 3; i++ )
|
||||
toxav_do(status_control.calls[0].Caller.av);
|
||||
|
||||
toxav_do(status_control.calls[0].Callee.av);
|
||||
toxav_do(status_control.calls[1].Callee.av);
|
||||
toxav_do(status_control.calls[2].Callee.av);
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
c_sleep(20);
|
||||
}
|
||||
|
@ -366,7 +356,7 @@ void test_AV_three_calls()
|
|||
tox_kill(callees[i]);
|
||||
|
||||
}
|
||||
// END_TEST
|
||||
END_TEST
|
||||
|
||||
|
||||
|
||||
|
@ -382,21 +372,22 @@ Suite *tox_suite(void)
|
|||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Suite *tox = tox_suite();
|
||||
// SRunner *test_runner = srunner_create(tox);
|
||||
//
|
||||
// setbuf(stdout, NULL);
|
||||
//
|
||||
// srunner_run_all(test_runner, CK_NORMAL);
|
||||
// int number_failed = srunner_ntests_failed(test_runner);
|
||||
//
|
||||
// srunner_free(test_runner);
|
||||
//
|
||||
// return number_failed;
|
||||
Suite *tox = tox_suite();
|
||||
SRunner *test_runner = srunner_create(tox);
|
||||
|
||||
test_AV_three_calls();
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
return 0;
|
||||
srunner_run_all(test_runner, CK_NORMAL);
|
||||
int number_failed = srunner_ntests_failed(test_runner);
|
||||
|
||||
srunner_free(test_runner);
|
||||
|
||||
return number_failed;
|
||||
|
||||
// test_AV_three_calls();
|
||||
|
||||
// return 0;
|
||||
}
|
||||
|
|
666
toxav/codec.c
666
toxav/codec.c
|
@ -32,11 +32,107 @@
|
|||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "msi.h"
|
||||
#include "rtp.h"
|
||||
#include "codec.h"
|
||||
|
||||
JitterBuffer *create_queue(unsigned int capacity)
|
||||
/* Assume 24 fps*/
|
||||
#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
|
||||
#define MAX_DECODE_TIME_US 0
|
||||
|
||||
// TODO this has to be exchanged in msi
|
||||
#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
|
||||
#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/
|
||||
#define VIDEOFRAME_HEADER_SIZE 0x2
|
||||
|
||||
/* FIXME: Might not be enough */
|
||||
#define VIDEO_DECODE_BUFFER_SIZE 20
|
||||
|
||||
#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; }
|
||||
#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
|
||||
|
||||
typedef ARRAY(uint8_t) Payload;
|
||||
|
||||
static PAIR(CSVideoCallback, void *) vpcallback;
|
||||
static PAIR(CSAudioCallback, void *) apcallback;
|
||||
|
||||
typedef struct {
|
||||
uint16_t size; /* Max size */
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
Payload **packets;
|
||||
} PayloadBuffer;
|
||||
|
||||
static _Bool buffer_full(const PayloadBuffer *b)
|
||||
{
|
||||
return (b->end + 1) % b->size == b->start;
|
||||
}
|
||||
|
||||
static _Bool buffer_empty(const PayloadBuffer *b)
|
||||
{
|
||||
return b->end == b->start;
|
||||
}
|
||||
|
||||
static void buffer_write(PayloadBuffer *b, Payload *p)
|
||||
{
|
||||
b->packets[b->end] = p;
|
||||
b->end = (b->end + 1) % b->size;
|
||||
|
||||
if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */
|
||||
}
|
||||
|
||||
static void buffer_read(PayloadBuffer *b, Payload **p)
|
||||
{
|
||||
*p = b->packets[b->start];
|
||||
b->start = (b->start + 1) % b->size;
|
||||
}
|
||||
|
||||
static void buffer_clear(PayloadBuffer *b)
|
||||
{
|
||||
while (!buffer_empty(b)) {
|
||||
Payload *p;
|
||||
buffer_read(b, &p);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static PayloadBuffer *buffer_new(int size)
|
||||
{
|
||||
PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1);
|
||||
|
||||
if (!buf) return NULL;
|
||||
|
||||
buf->size = size + 1; /* include empty elem */
|
||||
|
||||
if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void buffer_free(PayloadBuffer *b)
|
||||
{
|
||||
if (b) {
|
||||
buffer_clear(b);
|
||||
free(b->packets);
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
|
||||
/* JITTER BUFFER WORK */
|
||||
typedef struct _JitterBuffer {
|
||||
RTPMessage **queue;
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
uint16_t bottom;
|
||||
uint16_t top;
|
||||
} JitterBuffer;
|
||||
|
||||
static JitterBuffer *jbuf_new(uint32_t capacity)
|
||||
{
|
||||
unsigned int size = 1;
|
||||
|
||||
|
@ -58,7 +154,7 @@ JitterBuffer *create_queue(unsigned int capacity)
|
|||
return q;
|
||||
}
|
||||
|
||||
static void clear_queue(JitterBuffer *q)
|
||||
static void jbuf_clear(JitterBuffer *q)
|
||||
{
|
||||
for (; q->bottom != q->top; ++q->bottom) {
|
||||
if (q->queue[q->bottom % q->size]) {
|
||||
|
@ -68,25 +164,25 @@ static void clear_queue(JitterBuffer *q)
|
|||
}
|
||||
}
|
||||
|
||||
void terminate_queue(JitterBuffer *q)
|
||||
static void jbuf_free(JitterBuffer *q)
|
||||
{
|
||||
if (!q) return;
|
||||
|
||||
clear_queue(q);
|
||||
jbuf_clear(q);
|
||||
free(q->queue);
|
||||
free(q);
|
||||
}
|
||||
|
||||
void queue(JitterBuffer *q, RTPMessage *pk)
|
||||
static void jbuf_write(JitterBuffer *q, RTPMessage *m)
|
||||
{
|
||||
uint16_t sequnum = pk->header->sequnum;
|
||||
uint16_t sequnum = m->header->sequnum;
|
||||
|
||||
unsigned int num = sequnum % q->size;
|
||||
|
||||
if ((uint32_t)(sequnum - q->bottom) > q->size) {
|
||||
clear_queue(q);
|
||||
jbuf_clear(q);
|
||||
q->bottom = sequnum;
|
||||
q->queue[num] = pk;
|
||||
q->queue[num] = m;
|
||||
q->top = sequnum + 1;
|
||||
return;
|
||||
}
|
||||
|
@ -94,14 +190,16 @@ void queue(JitterBuffer *q, RTPMessage *pk)
|
|||
if (q->queue[num])
|
||||
return;
|
||||
|
||||
q->queue[num] = pk;
|
||||
q->queue[num] = m;
|
||||
|
||||
if ((sequnum - q->bottom) >= (q->top - q->bottom))
|
||||
q->top = sequnum + 1;
|
||||
}
|
||||
|
||||
/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
|
||||
RTPMessage *dequeue(JitterBuffer *q, int *success)
|
||||
/* Success is 0 when there is nothing to dequeue,
|
||||
* 1 when there's a good packet,
|
||||
* 2 when there's a lost packet */
|
||||
static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success)
|
||||
{
|
||||
if (q->top == q->bottom) {
|
||||
*success = 0;
|
||||
|
@ -128,8 +226,7 @@ RTPMessage *dequeue(JitterBuffer *q, int *success)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int init_video_decoder(CodecState *cs)
|
||||
static int init_video_decoder(CSSession *cs)
|
||||
{
|
||||
int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION);
|
||||
|
||||
|
@ -141,21 +238,230 @@ int init_video_decoder(CodecState *cs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int init_audio_decoder(CodecState *cs, uint32_t audio_channels)
|
||||
static int init_audio_decoder(CSSession *cs)
|
||||
{
|
||||
int rc;
|
||||
cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc );
|
||||
cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc );
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cs->audio_decoder_channels = audio_channels;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height)
|
||||
static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate)
|
||||
{
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg.rc_target_bitrate = video_bitrate;
|
||||
cfg.g_w = max_width;
|
||||
cfg.g_h = max_height;
|
||||
cfg.g_pass = VPX_RC_ONE_PASS;
|
||||
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
|
||||
cfg.g_lag_in_frames = 0;
|
||||
cfg.kf_min_dist = 0;
|
||||
cfg.kf_max_dist = 5;
|
||||
cfg.kf_mode = VPX_KF_AUTO;
|
||||
|
||||
cs->max_width = max_width;
|
||||
cs->max_height = max_height;
|
||||
|
||||
rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_audio_encoder(CSSession *cs)
|
||||
{
|
||||
int rc = OPUS_OK;
|
||||
cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate,
|
||||
cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc);
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float calculate_sum_sq (int16_t *n, uint16_t k)
|
||||
{
|
||||
float result = 0;
|
||||
uint16_t i = 0;
|
||||
|
||||
for ( ; i < k; i ++) result += (float) (n[i] * n[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* PUBLIC */
|
||||
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
|
||||
{
|
||||
if (!cs || !length || length > cs->max_video_frame_size) {
|
||||
LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cs->split_video_frame[0] = cs->frameid_out++;
|
||||
cs->split_video_frame[1] = 0;
|
||||
cs->processing_video_frame = payload;
|
||||
cs->processing_video_frame_size = length;
|
||||
|
||||
return ((length - 1) / cs->video_frame_piece_size) + 1;
|
||||
}
|
||||
|
||||
const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
|
||||
{
|
||||
if (!cs || !size) return NULL;
|
||||
|
||||
if (cs->processing_video_frame_size > cs->video_frame_piece_size) {
|
||||
memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
|
||||
cs->processing_video_frame,
|
||||
cs->video_frame_piece_size);
|
||||
|
||||
cs->processing_video_frame += cs->video_frame_piece_size;
|
||||
cs->processing_video_frame_size -= cs->video_frame_piece_size;
|
||||
|
||||
*size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE;
|
||||
} else {
|
||||
memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
|
||||
cs->processing_video_frame,
|
||||
cs->processing_video_frame_size);
|
||||
|
||||
*size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
|
||||
}
|
||||
|
||||
cs->split_video_frame[1]++;
|
||||
|
||||
return cs->split_video_frame;
|
||||
}
|
||||
|
||||
void cs_do(CSSession *cs)
|
||||
{
|
||||
if (!cs) return;
|
||||
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
/*
|
||||
if (!cs->active) {
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Iterate over whole buffers and call playback callback * /
|
||||
if (cs->abuf_ready) while (!DecodedAudioBuffer_empty(cs->abuf_ready)) {
|
||||
DecodedAudio* p;
|
||||
DecodedAudioBuffer_read(cs->abuf_ready, &p);
|
||||
if (apcallback.first)
|
||||
apcallback.first(cs->agent, cs->call_idx, p->data, p->size, apcallback.second);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
if (cs->vbuf_ready) while (!DecodedVideoBuffer_empty(cs->vbuf_ready)) {
|
||||
vpx_image_t* p;
|
||||
DecodedVideoBuffer_read(cs->vbuf_ready, &p);
|
||||
if (vpcallback.first)
|
||||
vpcallback.first(cs->agent, cs->call_idx, p, vpcallback.second);
|
||||
|
||||
vpx_img_free(p);
|
||||
}
|
||||
*/
|
||||
|
||||
Payload *p;
|
||||
int rc;
|
||||
|
||||
if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) {
|
||||
/* Decode audio */
|
||||
buffer_read(cs->abuf_raw, &p);
|
||||
|
||||
uint16_t fsize = (cs->audio_decoder_channels *
|
||||
(cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
|
||||
int16_t tmp[fsize];
|
||||
|
||||
rc = opus_decode(cs->audio_decoder, p->data, p->size, tmp, fsize, (p->size == 0));
|
||||
free(p);
|
||||
|
||||
if (rc < 0)
|
||||
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
|
||||
else
|
||||
/* Play */
|
||||
apcallback.first(cs->agent, cs->call_idx, tmp, rc, apcallback.second);
|
||||
}
|
||||
|
||||
if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
|
||||
/* Decode video */
|
||||
buffer_read(cs->vbuf_raw, &p);
|
||||
|
||||
rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
|
||||
free(p);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
|
||||
} else {
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter);
|
||||
|
||||
/* Play decoded images */
|
||||
for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
|
||||
vpcallback.first(cs->agent, cs->call_idx, dest, vpcallback.second);
|
||||
vpx_img_free(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
}
|
||||
|
||||
void cs_register_audio_callback(CSAudioCallback cb, void *data)
|
||||
{
|
||||
apcallback.first = cb;
|
||||
apcallback.second = data;
|
||||
}
|
||||
|
||||
void cs_register_video_callback(CSVideoCallback cb, void *data)
|
||||
{
|
||||
vpcallback.first = cb;
|
||||
vpcallback.second = data;
|
||||
}
|
||||
|
||||
int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height)
|
||||
{
|
||||
vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
|
||||
|
||||
|
@ -178,7 +484,7 @@ int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_
|
|||
return 0;
|
||||
}
|
||||
|
||||
int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate)
|
||||
int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
|
||||
{
|
||||
vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
|
||||
|
||||
|
@ -197,122 +503,122 @@ int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate)
|
||||
CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
|
||||
{
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
||||
CSSession *cs = calloc(sizeof(CSSession), 1);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg.rc_target_bitrate = video_bitrate;
|
||||
cfg.g_w = max_width;
|
||||
cfg.g_h = max_height;
|
||||
cfg.g_pass = VPX_RC_ONE_PASS;
|
||||
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
|
||||
cfg.g_lag_in_frames = 0;
|
||||
cfg.kf_min_dist = 0;
|
||||
cfg.kf_max_dist = 5;
|
||||
cfg.kf_mode = VPX_KF_AUTO;
|
||||
|
||||
cs->max_width = max_width;
|
||||
cs->max_height = max_height;
|
||||
cs->bitrate = video_bitrate;
|
||||
|
||||
rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_audio_encoder(CodecState *cs, uint32_t audio_channels)
|
||||
{
|
||||
int rc = OPUS_OK;
|
||||
cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc);
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cs->audio_encoder_channels = audio_channels;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
CodecState *codec_init_session ( uint32_t audio_bitrate,
|
||||
uint16_t audio_frame_duration,
|
||||
uint32_t audio_sample_rate,
|
||||
uint32_t encoder_audio_channels,
|
||||
uint32_t decoder_audio_channels,
|
||||
uint32_t audio_VAD_tolerance_ms,
|
||||
uint16_t max_video_width,
|
||||
uint16_t max_video_height,
|
||||
uint32_t video_bitrate )
|
||||
{
|
||||
CodecState *retu = calloc(sizeof(CodecState), 1);
|
||||
|
||||
if (!retu) return NULL;
|
||||
|
||||
retu->audio_bitrate = audio_bitrate;
|
||||
retu->audio_sample_rate = audio_sample_rate;
|
||||
|
||||
/* Encoders */
|
||||
if (!max_video_width || !max_video_height) { /* Disable video */
|
||||
/*video_width = 320;
|
||||
video_height = 240; */
|
||||
} else {
|
||||
retu->capabilities |= ( 0 == init_video_encoder(retu, max_video_width, max_video_height,
|
||||
video_bitrate) ) ? v_encoding : 0;
|
||||
retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0;
|
||||
}
|
||||
|
||||
retu->capabilities |= ( 0 == init_audio_encoder(retu, encoder_audio_channels) ) ? a_encoding : 0;
|
||||
retu->capabilities |= ( 0 == init_audio_decoder(retu, decoder_audio_channels) ) ? a_decoding : 0;
|
||||
|
||||
if ( retu->capabilities == 0 ) { /* everything failed */
|
||||
free (retu);
|
||||
if (!cs) {
|
||||
LOGGER_WARNING("Allocation failed! Application might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
|
||||
LOGGER_WARNING("Jitter buffer creaton failed!");
|
||||
goto error;
|
||||
}
|
||||
|
||||
retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ?
|
||||
audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration;
|
||||
cs->audio_encoder_bitrate = cs_self->audio_bitrate;
|
||||
cs->audio_encoder_sample_rate = cs_self->audio_sample_rate;
|
||||
cs->audio_encoder_channels = cs_self->audio_channels;
|
||||
cs->audio_encoder_frame_duration = cs_self->audio_frame_duration;
|
||||
|
||||
return retu;
|
||||
cs->audio_decoder_bitrate = cs_peer->audio_bitrate;
|
||||
cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate;
|
||||
cs->audio_decoder_channels = cs_peer->audio_channels;
|
||||
cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
|
||||
|
||||
|
||||
cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? a_encoding : 0;
|
||||
cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? a_decoding : 0;
|
||||
|
||||
if ( !(cs->capabilities & a_encoding) || !(cs->capabilities & a_decoding) ) goto error;
|
||||
|
||||
if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error;
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0) goto error;
|
||||
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
|
||||
if (pthread_mutexattr_destroy(&attr) != 0)
|
||||
LOGGER_WARNING("Failed to destroy mutex attribute!");
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(cs->queue_mutex, &attr) != 0) {
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((cs->support_video = has_video)) {
|
||||
cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
|
||||
cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
|
||||
|
||||
cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
|
||||
cs_self->max_video_height, cs_self->video_bitrate) ) ? v_encoding : 0;
|
||||
cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? v_decoding : 0;
|
||||
|
||||
if ( !(cs->capabilities & v_encoding) || !(cs->capabilities & v_decoding) ) goto error;
|
||||
|
||||
if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
|
||||
|
||||
if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) )
|
||||
goto error;
|
||||
|
||||
if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
|
||||
}
|
||||
|
||||
if (pthread_mutexattr_destroy(&attr) != 0)
|
||||
LOGGER_WARNING("Failed to destroy mutex attribute!");
|
||||
|
||||
|
||||
cs->active = 1;
|
||||
return cs;
|
||||
|
||||
error:
|
||||
LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
|
||||
|
||||
buffer_free(cs->abuf_raw);
|
||||
|
||||
if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
|
||||
|
||||
if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder);
|
||||
|
||||
|
||||
if (has_video) {
|
||||
if ( cs->capabilities & v_decoding ) vpx_codec_destroy(&cs->v_decoder);
|
||||
|
||||
if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder);
|
||||
|
||||
buffer_free(cs->vbuf_raw);
|
||||
|
||||
free(cs->frame_buf);
|
||||
free(cs->split_video_frame);
|
||||
}
|
||||
|
||||
jbuf_free(cs->j_buf);
|
||||
free(cs);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void codec_terminate_session ( CodecState *cs )
|
||||
void cs_kill(CSSession *cs)
|
||||
{
|
||||
if (!cs) return;
|
||||
|
||||
/* Lock running mutex and signal that cs is no longer active */
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
cs->active = 0;
|
||||
|
||||
/* Wait threads to close */
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
|
||||
pthread_mutex_destroy(cs->queue_mutex);
|
||||
|
||||
|
||||
if ( cs->audio_encoder )
|
||||
opus_encoder_destroy(cs->audio_encoder);
|
||||
|
||||
|
@ -325,21 +631,21 @@ void codec_terminate_session ( CodecState *cs )
|
|||
if ( cs->capabilities & v_encoding )
|
||||
vpx_codec_destroy(&cs->v_encoder);
|
||||
|
||||
jbuf_free(cs->j_buf);
|
||||
buffer_free(cs->abuf_raw);
|
||||
buffer_free(cs->vbuf_raw);
|
||||
free(cs->frame_buf);
|
||||
|
||||
LOGGER_DEBUG("Terminated codec state: %p", cs);
|
||||
free(cs);
|
||||
}
|
||||
|
||||
static float calculate_sum_sq (int16_t *n, uint16_t k)
|
||||
void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration)
|
||||
{
|
||||
float result = 0;
|
||||
uint16_t i = 0;
|
||||
|
||||
for ( ; i < k; i ++) result += (float) (n[i] * n[i]);
|
||||
|
||||
return result;
|
||||
cs->EVAD_tolerance = treshold > frame_duration ? treshold / frame_duration : frame_duration;
|
||||
}
|
||||
|
||||
int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy)
|
||||
int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy)
|
||||
{
|
||||
float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size;
|
||||
|
||||
|
@ -355,3 +661,103 @@ int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Called from RTP */
|
||||
void queue_message(RTPSession *session, RTPMessage *msg)
|
||||
{
|
||||
CSSession *cs = session->cs;
|
||||
|
||||
if (!cs || !cs->active) return;
|
||||
|
||||
/* Audio */
|
||||
if (session->payload_type == type_audio % 128) {
|
||||
jbuf_write(cs->j_buf, msg);
|
||||
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
int success = 0;
|
||||
|
||||
while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
|
||||
Payload *p;
|
||||
|
||||
if (success == 2) {
|
||||
p = malloc(sizeof(Payload));
|
||||
|
||||
if (p) p->size = 0;
|
||||
|
||||
} else {
|
||||
p = malloc(sizeof(Payload) + msg->length);
|
||||
|
||||
if (p) {
|
||||
p->size = msg->length;
|
||||
memcpy(p->data, msg->data, msg->length);
|
||||
}
|
||||
|
||||
rtp_free_msg(NULL, msg);
|
||||
}
|
||||
|
||||
if (p) {
|
||||
buffer_write(cs->abuf_raw, p);
|
||||
} else {
|
||||
LOGGER_WARNING("Allocation failed! Program might misbehave!");
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
}
|
||||
/* Video */
|
||||
else {
|
||||
uint8_t *packet = msg->data;
|
||||
uint32_t packet_size = msg->length;
|
||||
|
||||
if (packet_size < VIDEOFRAME_HEADER_SIZE)
|
||||
goto end;
|
||||
|
||||
if (packet[0] > cs->frameid_in || (msg->header->timestamp > cs->last_timestamp)) { /* New frame */
|
||||
/* Flush last frames' data and get ready for this frame */
|
||||
Payload *p = malloc(sizeof(Payload) + cs->frame_size);
|
||||
|
||||
if (p) {
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
|
||||
if (buffer_full(cs->vbuf_raw)) {
|
||||
LOGGER_DEBUG("Dropped video frame");
|
||||
Payload *tp;
|
||||
buffer_read(cs->vbuf_raw, &tp);
|
||||
free(tp);
|
||||
} else {
|
||||
p->size = cs->frame_size;
|
||||
memcpy(p->data, cs->frame_buf, cs->frame_size);
|
||||
}
|
||||
|
||||
buffer_write(cs->vbuf_raw, p);
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
} else {
|
||||
LOGGER_WARNING("Allocation failed! Program might misbehave!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
cs->last_timestamp = msg->header->timestamp;
|
||||
cs->frameid_in = packet[0];
|
||||
memset(cs->frame_buf, 0, cs->frame_size);
|
||||
cs->frame_size = 0;
|
||||
|
||||
} else if (packet[0] < cs->frameid_in) { /* Old frame; drop */
|
||||
LOGGER_DEBUG("Old packet: %u", packet[0]);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Otherwise it's part of the frame so just process */
|
||||
/* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
|
||||
memcpy(cs->frame_buf + cs->frame_size,
|
||||
packet + VIDEOFRAME_HEADER_SIZE,
|
||||
packet_size - VIDEOFRAME_HEADER_SIZE);
|
||||
|
||||
cs->frame_size += packet_size - VIDEOFRAME_HEADER_SIZE;
|
||||
|
||||
end:
|
||||
rtp_free_msg(NULL, msg);
|
||||
}
|
||||
}
|
118
toxav/codec.h
118
toxav/codec.h
|
@ -24,6 +24,9 @@
|
|||
#ifndef _CODEC_H_
|
||||
#define _CODEC_H_
|
||||
|
||||
#include "toxav.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
|
@ -32,23 +35,30 @@
|
|||
#include <vpx/vpx_encoder.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
#include <vpx/vp8cx.h>
|
||||
#include <vpx/vpx_image.h>
|
||||
#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
|
||||
#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
|
||||
|
||||
/* Audio encoding/decoding */
|
||||
#include <opus.h>
|
||||
|
||||
typedef enum _Capabilities {
|
||||
none,
|
||||
typedef void (*CSAudioCallback) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
|
||||
typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
|
||||
|
||||
typedef enum _CsCapabilities {
|
||||
a_encoding = 1 << 0,
|
||||
a_decoding = 1 << 1,
|
||||
v_encoding = 1 << 2,
|
||||
v_decoding = 1 << 3
|
||||
} Capabilities;
|
||||
} CsCapabilities;
|
||||
|
||||
extern const uint16_t min_jbuf_size;
|
||||
typedef struct _CSSession {
|
||||
|
||||
typedef struct _CodecState {
|
||||
/* VIDEO
|
||||
*
|
||||
*
|
||||
*/
|
||||
int support_video;
|
||||
|
||||
/* video encoding */
|
||||
vpx_codec_ctx_t v_encoder;
|
||||
|
@ -56,61 +66,95 @@ typedef struct _CodecState {
|
|||
|
||||
/* video decoding */
|
||||
vpx_codec_ctx_t v_decoder;
|
||||
int bitrate;
|
||||
int max_width;
|
||||
int max_height;
|
||||
|
||||
|
||||
/* Data handling */
|
||||
uint8_t *frame_buf; /* buffer for split video payloads */
|
||||
uint32_t frame_size; /* largest address written to in frame_buf for current input frame*/
|
||||
uint8_t frameid_in, frameid_out; /* id of input and output video frame */
|
||||
uint32_t last_timestamp; /* calculating cycles */
|
||||
|
||||
/* Limits */
|
||||
uint32_t video_frame_piece_size;
|
||||
uint32_t max_video_frame_size;
|
||||
|
||||
/* Reassembling */
|
||||
uint8_t *split_video_frame;
|
||||
const uint8_t *processing_video_frame;
|
||||
uint16_t processing_video_frame_size;
|
||||
|
||||
|
||||
|
||||
/* AUDIO
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* audio encoding */
|
||||
OpusEncoder *audio_encoder;
|
||||
int audio_bitrate;
|
||||
int audio_sample_rate;
|
||||
int audio_encoder_bitrate;
|
||||
int audio_encoder_sample_rate;
|
||||
int audio_encoder_frame_duration;
|
||||
int audio_encoder_channels;
|
||||
|
||||
/* audio decoding */
|
||||
OpusDecoder *audio_decoder;
|
||||
int audio_decoder_bitrate;
|
||||
int audio_decoder_sample_rate;
|
||||
int audio_decoder_frame_duration;
|
||||
int audio_decoder_channels;
|
||||
|
||||
uint64_t capabilities; /* supports*/
|
||||
struct _JitterBuffer *j_buf;
|
||||
|
||||
|
||||
/* Voice activity detection */
|
||||
uint32_t EVAD_tolerance; /* In frames */
|
||||
uint32_t EVAD_tolerance_cr;
|
||||
} CodecState;
|
||||
|
||||
|
||||
typedef struct _JitterBuffer {
|
||||
RTPMessage **queue;
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
uint16_t bottom;
|
||||
uint16_t top;
|
||||
} JitterBuffer;
|
||||
|
||||
JitterBuffer *create_queue(unsigned int capacity);
|
||||
void terminate_queue(JitterBuffer *q);
|
||||
void queue(JitterBuffer *q, RTPMessage *pk);
|
||||
RTPMessage *dequeue(JitterBuffer *q, int *success);
|
||||
/* OTHER
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
uint64_t capabilities; /* supports*/
|
||||
|
||||
CodecState *codec_init_session ( uint32_t audio_bitrate,
|
||||
uint16_t audio_frame_duration,
|
||||
uint32_t audio_sample_rate,
|
||||
uint32_t encoder_audio_channels,
|
||||
uint32_t decoder_audio_channels,
|
||||
uint32_t audio_VAD_tolerance_ms,
|
||||
uint16_t max_video_width,
|
||||
uint16_t max_video_height,
|
||||
uint32_t video_bitrate );
|
||||
/* Buffering */
|
||||
void *abuf_raw, *vbuf_raw; /* Un-decoded data */
|
||||
_Bool active;
|
||||
pthread_mutex_t queue_mutex[1];
|
||||
|
||||
void codec_terminate_session(CodecState *cs);
|
||||
void *agent; /* Pointer to ToxAv */
|
||||
int32_t call_idx;
|
||||
} CSSession;
|
||||
|
||||
CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video);
|
||||
void cs_kill(CSSession *cs);
|
||||
|
||||
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length);
|
||||
const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size);
|
||||
|
||||
/**
|
||||
* Call playback callbacks
|
||||
*/
|
||||
void cs_do(CSSession *cs);
|
||||
|
||||
void cs_register_audio_callback(CSAudioCallback cb, void *data);
|
||||
void cs_register_video_callback(CSVideoCallback cb, void *data);
|
||||
|
||||
/* Reconfigure video encoder; return 0 on success or -1 on failure. */
|
||||
int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height);
|
||||
int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate);
|
||||
|
||||
/* Reconfigure video encoder
|
||||
return 0 on success.
|
||||
return -1 on failure. */
|
||||
int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height);
|
||||
int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate);
|
||||
|
||||
/* Calculate energy and return 1 if has voice, 0 if not */
|
||||
int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy);
|
||||
int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy);
|
||||
void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration);
|
||||
|
||||
|
||||
/* Internal. Called from rtp_handle_message */
|
||||
void queue_message(RTPSession *session, RTPMessage *msg);
|
||||
#endif /* _CODEC_H_ */
|
||||
|
|
821
toxav/msi.c
821
toxav/msi.c
File diff suppressed because it is too large
Load Diff
166
toxav/msi.h
166
toxav/msi.h
|
@ -31,9 +31,8 @@ typedef uint8_t MSICallIDType[12];
|
|||
typedef uint8_t MSIReasonStrType[255];
|
||||
typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Call type identifier. Also used as rtp callback prefix.
|
||||
* Call type identifier. Also used as rtp callback prefix.
|
||||
*/
|
||||
typedef enum {
|
||||
type_audio = 192,
|
||||
|
@ -42,7 +41,7 @@ typedef enum {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Call state identifiers.
|
||||
* Call state identifiers.
|
||||
*/
|
||||
typedef enum {
|
||||
call_inviting, /* when sending call invite */
|
||||
|
@ -55,7 +54,7 @@ typedef enum {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Encoding settings.
|
||||
* Encoding settings.
|
||||
*/
|
||||
typedef struct _MSICodecSettings {
|
||||
MSICallType call_type;
|
||||
|
@ -72,29 +71,24 @@ typedef struct _MSICodecSettings {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Callbacks ids that handle the states
|
||||
* Callbacks ids that handle the states
|
||||
*/
|
||||
typedef enum {
|
||||
/* 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
|
||||
MSI_OnInvite, /* Incoming call */
|
||||
MSI_OnRinging, /* When peer is ready to accept/reject the call */
|
||||
MSI_OnStart, /* Call (RTP transmission) started */
|
||||
MSI_OnCancel, /* The side that initiated call canceled invite */
|
||||
MSI_OnReject, /* The side that was invited rejected the call */
|
||||
MSI_OnEnd, /* Call that was active ended */
|
||||
MSI_OnRequestTimeout, /* When the requested action didn't get response in specified time */
|
||||
MSI_OnPeerTimeout, /* Peer timed out; stop the call */
|
||||
MSI_OnPeerCSChange, /* Peer requested Csettings change */
|
||||
MSI_OnSelfCSChange /* Csettings change confirmation */
|
||||
} MSICallbackID;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks container
|
||||
* Callbacks container
|
||||
*/
|
||||
typedef struct _MSICallbackCont {
|
||||
MSICallbackType function;
|
||||
|
@ -102,8 +96,7 @@ typedef struct _MSICallbackCont {
|
|||
} MSICallbackCont;
|
||||
|
||||
/**
|
||||
* @brief The call struct.
|
||||
*
|
||||
* The call struct.
|
||||
*/
|
||||
typedef struct _MSICall { /* Call info structure */
|
||||
struct _MSISession *session; /* Session pointer */
|
||||
|
@ -120,8 +113,6 @@ typedef struct _MSICall { /* Call info structure */
|
|||
int request_timer_id; /* Timer id for outgoing request/action */
|
||||
int ringing_timer_id; /* Timer id for ringing timeout */
|
||||
|
||||
|
||||
pthread_mutex_t mutex; /* */
|
||||
uint32_t *peers;
|
||||
uint16_t peer_count;
|
||||
|
||||
|
@ -130,8 +121,7 @@ typedef struct _MSICall { /* Call info structure */
|
|||
|
||||
|
||||
/**
|
||||
* @brief Control session struct
|
||||
*
|
||||
* Control session struct
|
||||
*/
|
||||
typedef struct _MSISession {
|
||||
|
||||
|
@ -148,120 +138,66 @@ typedef struct _MSISession {
|
|||
pthread_mutex_t mutex;
|
||||
|
||||
void *timer_handler;
|
||||
MSICallbackCont callbacks[11]; /* Callbacks used by this session */
|
||||
MSICallbackCont callbacks[10]; /* Callbacks used by this session */
|
||||
} MSISession;
|
||||
|
||||
/**
|
||||
* @brief Callback setter.
|
||||
*
|
||||
* @param session The container.
|
||||
* @param callback The callback.
|
||||
* @param id The id.
|
||||
* @return void
|
||||
* Start the control session.
|
||||
*/
|
||||
MSISession *msi_new ( Messenger *messenger, int32_t max_calls );
|
||||
|
||||
/**
|
||||
* Terminate control session.
|
||||
*/
|
||||
int msi_kill ( MSISession *session );
|
||||
|
||||
/**
|
||||
* Callback setter.
|
||||
*/
|
||||
void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the control session.
|
||||
*
|
||||
* @param messenger Tox* object.
|
||||
* @param max_calls Amount of calls possible
|
||||
* @return MSISession* The created session.
|
||||
* @retval NULL Error occurred.
|
||||
* Send invite request to friend_id.
|
||||
*/
|
||||
MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Terminate control session.
|
||||
*
|
||||
* @param session The session
|
||||
* @return int
|
||||
*/
|
||||
int msi_terminate_session ( MSISession *session );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send invite request to friend_id.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index Set to new call index.
|
||||
* @param call_type Type of the call. Audio or Video(both audio and video)
|
||||
* @param rngsec Ringing timeout.
|
||||
* @param friend_id The friend.
|
||||
* @return int
|
||||
*/
|
||||
int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec,
|
||||
int msi_invite ( MSISession *session,
|
||||
int32_t *call_index,
|
||||
const MSICSettings *csettings,
|
||||
uint32_t rngsec,
|
||||
uint32_t friend_id );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Hangup active call.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index To which call is this action handled.
|
||||
* @return int
|
||||
* @retval -1 Error occurred.
|
||||
* @retval 0 Success.
|
||||
* Hangup active call.
|
||||
*/
|
||||
int msi_hangup ( MSISession *session, int32_t call_index );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Answer active call request.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index To which call is this action handled.
|
||||
* @param call_type Answer with Audio or Video(both).
|
||||
* @return int
|
||||
* Answer active call request.
|
||||
*/
|
||||
int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings );
|
||||
|
||||
int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
|
||||
|
||||
/**
|
||||
* @brief Cancel request.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index To which call is this action handled.
|
||||
* @param peer To which peer.
|
||||
* @param reason Set optional reason header. Pass NULL if none.
|
||||
* @return int
|
||||
* Cancel request.
|
||||
*/
|
||||
int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reject request.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index To which call is this action handled.
|
||||
* @param reason Set optional reason header. Pass NULL if none.
|
||||
* @return int
|
||||
* Reject incoming call.
|
||||
*/
|
||||
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_csettings ( MSISession *session, int32_t call_index, MSICSettings csettings );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Terminate the current call.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param call_index To which call is this action handled.
|
||||
* @return int
|
||||
* Terminate the call.
|
||||
*/
|
||||
int msi_stopcall ( MSISession *session, int32_t call_index );
|
||||
|
||||
/**
|
||||
* Change codec settings of the current call.
|
||||
*/
|
||||
int msi_change_csettings ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
|
||||
|
||||
/**
|
||||
* Main msi loop
|
||||
*/
|
||||
void msi_do( MSISession *session );
|
||||
|
||||
#endif /* __TOXMSI */
|
||||
|
|
130
toxav/rtp.c
130
toxav/rtp.c
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "rtp.h"
|
||||
#include <stdlib.h>
|
||||
void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg);
|
||||
void queue_message(RTPSession *_session, RTPMessage *_msg);
|
||||
|
||||
#define size_32 4
|
||||
|
||||
|
@ -47,15 +47,9 @@ void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg);
|
|||
#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
|
||||
|
||||
/**
|
||||
* @brief Checks if message came in late.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param msg The message.
|
||||
* @return int
|
||||
* @retval -1 The message came in order.
|
||||
* @retval 0 The message came late.
|
||||
* Checks if message came in late.
|
||||
*/
|
||||
inline__ int check_late_message (RTPSession *session, RTPMessage *msg)
|
||||
static int check_late_message (RTPSession *session, RTPMessage *msg)
|
||||
{
|
||||
/*
|
||||
* Check Sequence number. If this new msg has lesser number then the session->rsequnum
|
||||
|
@ -67,12 +61,7 @@ inline__ int check_late_message (RTPSession *session, RTPMessage *msg)
|
|||
|
||||
|
||||
/**
|
||||
* @brief Extracts header from payload.
|
||||
*
|
||||
* @param payload The payload.
|
||||
* @param length The size of payload.
|
||||
* @return RTPHeader* Extracted header.
|
||||
* @retval NULL Error occurred while extracting header.
|
||||
* Extracts header from payload.
|
||||
*/
|
||||
RTPHeader *extract_header ( const uint8_t *payload, int length )
|
||||
{
|
||||
|
@ -147,12 +136,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts external header from payload. Must be called AFTER extract_header()!
|
||||
*
|
||||
* @param payload The ITERATED payload.
|
||||
* @param length The size of payload.
|
||||
* @return RTPExtHeader* Extracted extension header.
|
||||
* @retval NULL Error occurred while extracting extension header.
|
||||
* Extracts external header from payload. Must be called AFTER extract_header()!
|
||||
*/
|
||||
RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
|
||||
{
|
||||
|
@ -200,11 +184,7 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Adds header to payload. Make sure _payload_ has enough space.
|
||||
*
|
||||
* @param header The header.
|
||||
* @param payload The payload.
|
||||
* @return uint8_t* Iterated position.
|
||||
* Adds header to payload. Make sure _payload_ has enough space.
|
||||
*/
|
||||
uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
|
||||
{
|
||||
|
@ -245,11 +225,7 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Adds extension header to payload. Make sure _payload_ has enough space.
|
||||
*
|
||||
* @param header The header.
|
||||
* @param payload The payload.
|
||||
* @return uint8_t* Iterated position.
|
||||
* Adds extension header to payload. Make sure _payload_ has enough space.
|
||||
*/
|
||||
uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
|
||||
{
|
||||
|
@ -279,10 +255,7 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Builds header from control session values.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @return RTPHeader* Created header.
|
||||
* Builds header from control session values.
|
||||
*/
|
||||
RTPHeader *build_header ( RTPSession *session )
|
||||
{
|
||||
|
@ -316,16 +289,8 @@ RTPHeader *build_header ( RTPSession *session )
|
|||
|
||||
|
||||
/**
|
||||
* @brief Parses data into RTPMessage struct. Stores headers separately from the payload data
|
||||
* and so the length variable is set accordingly. _sequnum_ argument is
|
||||
* passed by the handle_packet() since it's parsed already.
|
||||
*
|
||||
* @param session Control session.
|
||||
* @param sequnum Sequence number that's parsed from payload in handle_packet()
|
||||
* @param data Payload data.
|
||||
* @param length Payload size.
|
||||
* @return RTPMessage*
|
||||
* @retval NULL Error occurred.
|
||||
* Parses data into RTPMessage struct. Stores headers separately from the payload data
|
||||
* and so the length variable is set accordingly.
|
||||
*/
|
||||
RTPMessage *msg_parse ( const uint8_t *data, int length )
|
||||
{
|
||||
|
@ -373,15 +338,7 @@ RTPMessage *msg_parse ( const uint8_t *data, int length )
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for networking core.
|
||||
*
|
||||
* @param object RTPSession object.
|
||||
* @param ip_port Where the message comes from.
|
||||
* @param data Message data.
|
||||
* @param length Message length.
|
||||
* @return int
|
||||
* @retval -1 Error occurred.
|
||||
* @retval 0 Success.
|
||||
* Callback for networking core.
|
||||
*/
|
||||
int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length )
|
||||
{
|
||||
|
@ -406,22 +363,13 @@ int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length )
|
|||
_session->timestamp = _msg->header->timestamp;
|
||||
}
|
||||
|
||||
toxav_handle_packet(_session, _msg);
|
||||
queue_message(_session, _msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stores headers and payload data in one container ( data )
|
||||
* and the length is set accordingly. Returned message is used for sending _only_.
|
||||
*
|
||||
* @param session The control session.
|
||||
* @param data Payload data to send ( This is what you pass ).
|
||||
* @param length Size of the payload data.
|
||||
* @return RTPMessage* Created message.
|
||||
* @retval NULL Error occurred.
|
||||
* Allocate message and store data there
|
||||
*/
|
||||
RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
|
||||
{
|
||||
|
@ -472,28 +420,15 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sends data to _RTPSession::dest
|
||||
*
|
||||
* @param session The session.
|
||||
* @param messenger Tox* object.
|
||||
* @param data The payload.
|
||||
* @param length Size of the payload.
|
||||
* @return int
|
||||
* @retval -1 On error.
|
||||
* @retval 0 On success.
|
||||
*/
|
||||
|
||||
int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length )
|
||||
{
|
||||
RTPMessage *msg = rtp_new_message (session, data, length);
|
||||
|
||||
if ( !msg ) {
|
||||
LOGGER_WARNING("No session!");
|
||||
return -1;
|
||||
}
|
||||
if ( !msg ) return -1;
|
||||
|
||||
if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) {
|
||||
LOGGER_WARNING("Failed to send full packet! std error: %s", strerror(errno));
|
||||
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
|
||||
rtp_free_msg ( session, msg );
|
||||
return -1;
|
||||
}
|
||||
|
@ -506,15 +441,6 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Speaks for it self.
|
||||
*
|
||||
* @param session The control session msg belongs to. You set it as NULL when freeing recved messages.
|
||||
* Otherwise set it to session the message was created from.
|
||||
* @param msg The message.
|
||||
* @return void
|
||||
*/
|
||||
void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
|
||||
{
|
||||
if ( !session ) {
|
||||
|
@ -533,17 +459,7 @@ 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.
|
||||
*
|
||||
* @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
|
||||
* @param messenger Tox* object.
|
||||
* @param friend_num Friend id.
|
||||
* @return RTPSession* Created control session.
|
||||
* @retval NULL Error occurred.
|
||||
*/
|
||||
RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num )
|
||||
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
|
||||
{
|
||||
RTPSession *_retu = calloc(1, sizeof(RTPSession));
|
||||
|
||||
|
@ -593,17 +509,7 @@ RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int frien
|
|||
return _retu;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Terminate the session.
|
||||
*
|
||||
* @param session The session.
|
||||
* @param messenger The messenger who owns the session
|
||||
* @return int
|
||||
* @retval -1 Error occurred.
|
||||
* @retval 0 Success.
|
||||
*/
|
||||
void rtp_terminate_session ( RTPSession *session, Messenger *messenger )
|
||||
void rtp_kill ( RTPSession *session, Messenger *messenger )
|
||||
{
|
||||
if ( !session ) return;
|
||||
|
||||
|
|
89
toxav/rtp.h
89
toxav/rtp.h
|
@ -24,19 +24,15 @@
|
|||
|
||||
#define RTP_VERSION 2
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
// #include <pthread.h>
|
||||
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/network.h"
|
||||
#include "../toxcore/net_crypto.h"
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
#define MAX_SEQU_NUM 65535
|
||||
#define MAX_RTP_SIZE 65535
|
||||
|
||||
/**
|
||||
* @brief Standard rtp header
|
||||
*
|
||||
* Standard rtp header
|
||||
*/
|
||||
|
||||
typedef struct _RTPHeader {
|
||||
|
@ -52,8 +48,7 @@ typedef struct _RTPHeader {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Standard rtp extension header.
|
||||
*
|
||||
* Standard rtp extension header.
|
||||
*/
|
||||
typedef struct _RTPExtHeader {
|
||||
uint16_t type; /* Extension profile */
|
||||
|
@ -64,8 +59,7 @@ typedef struct _RTPExtHeader {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Standard rtp message.
|
||||
*
|
||||
* Standard rtp message.
|
||||
*/
|
||||
typedef struct _RTPMessage {
|
||||
RTPHeader *header;
|
||||
|
@ -79,7 +73,7 @@ typedef struct _RTPMessage {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Our main session descriptor.
|
||||
* Our main session descriptor.
|
||||
* It measures the session variables and controls
|
||||
* the entire session. There are functions for manipulating
|
||||
* the session so tend to use those instead of directly modifying
|
||||
|
@ -109,88 +103,31 @@ typedef struct _RTPSession {
|
|||
uint8_t prefix;
|
||||
|
||||
int dest;
|
||||
int32_t call_index;
|
||||
struct _ToxAv *av;
|
||||
|
||||
struct _CSSession *cs;
|
||||
|
||||
} RTPSession;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Release all messages held by session.
|
||||
*
|
||||
* @param session The session.
|
||||
* @return int
|
||||
* @retval -1 Error occurred.
|
||||
* @retval 0 Success.
|
||||
* Must be called before calling any other rtp function.
|
||||
*/
|
||||
int rtp_release_session_recv ( RTPSession *session );
|
||||
|
||||
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num );
|
||||
|
||||
/**
|
||||
* @brief Call this to change queue limit
|
||||
*
|
||||
* @param session The session
|
||||
* @param limit new limit
|
||||
* @return void
|
||||
* Terminate the session.
|
||||
*/
|
||||
void rtp_queue_adjust_limit ( RTPSession *session, uint64_t limit );
|
||||
void rtp_kill ( RTPSession *session, Messenger *messenger );
|
||||
|
||||
/**
|
||||
* @brief Get's oldest message in the list.
|
||||
*
|
||||
* @param session Where the list is.
|
||||
* @return RTPMessage* The message. You need to call rtp_msg_free() to free it.
|
||||
* @retval NULL No messages in the list, or no list.
|
||||
*/
|
||||
RTPMessage *rtp_recv_msg ( RTPSession *session );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sends msg to _RTPSession::dest
|
||||
*
|
||||
* @param session The session.
|
||||
* @param msg The message
|
||||
* @param messenger Tox* object.
|
||||
* @return int
|
||||
* @retval -1 On error.
|
||||
* @retval 0 On success.
|
||||
* Sends msg to _RTPSession::dest
|
||||
*/
|
||||
int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Speaks for it self.
|
||||
*
|
||||
* @param session The control session msg belongs to. It can be NULL.
|
||||
* @param msg The message.
|
||||
* @return void
|
||||
* Dealloc msg.
|
||||
*/
|
||||
void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
|
||||
|
||||
/**
|
||||
* @brief Must be called before calling any other rtp function. It's used
|
||||
* to initialize RTP control session.
|
||||
*
|
||||
* @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
|
||||
* @param messenger Tox* object.
|
||||
* @param friend_num Friend id.
|
||||
* @return RTPSession* Created control session.
|
||||
* @retval NULL Error occurred.
|
||||
*/
|
||||
RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Terminate the session.
|
||||
*
|
||||
* @param session The session.
|
||||
* @param messenger The messenger who owns the session
|
||||
* @return int
|
||||
* @retval -1 Error occurred.
|
||||
* @retval 0 Success.
|
||||
*/
|
||||
void rtp_terminate_session ( RTPSession *session, Messenger *messenger );
|
||||
|
||||
|
||||
|
||||
#endif /* __TOXRTP */
|
||||
|
|
979
toxav/toxav.c
979
toxav/toxav.c
File diff suppressed because it is too large
Load Diff
335
toxav/toxav.h
335
toxav/toxav.h
|
@ -28,11 +28,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* vpx_image_t */
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
|
||||
typedef struct _ToxAv ToxAv;
|
||||
typedef struct vpx_image vpx_image_t;
|
||||
typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
|
||||
typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
|
||||
typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
|
||||
|
||||
#ifndef __TOX_DEFINED__
|
||||
#define __TOX_DEFINED__
|
||||
|
@ -43,34 +43,28 @@ typedef struct Tox Tox;
|
|||
|
||||
|
||||
/**
|
||||
* @brief Callbacks ids that handle the call states.
|
||||
* Callbacks ids that handle the call states.
|
||||
*/
|
||||
typedef enum {
|
||||
/* Requests */
|
||||
av_OnInvite,
|
||||
av_OnStart,
|
||||
av_OnCancel,
|
||||
av_OnReject,
|
||||
av_OnEnd,
|
||||
|
||||
/* Responses */
|
||||
av_OnRinging,
|
||||
av_OnStarting,
|
||||
av_OnEnding,
|
||||
|
||||
/* Protocol */
|
||||
av_OnRequestTimeout,
|
||||
av_OnPeerTimeout,
|
||||
av_OnMediaChange
|
||||
av_OnInvite, /* Incoming call */
|
||||
av_OnRinging, /* When peer is ready to accept/reject the call */
|
||||
av_OnStart, /* Call (RTP transmission) started */
|
||||
av_OnCancel, /* The side that initiated call canceled invite */
|
||||
av_OnReject, /* The side that was invited rejected the call */
|
||||
av_OnEnd, /* Call that was active ended */
|
||||
av_OnRequestTimeout, /* When the requested action didn't get response in specified time */
|
||||
av_OnPeerTimeout, /* Peer timed out; stop the call */
|
||||
av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */
|
||||
av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */
|
||||
} ToxAvCallbackID;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Call type identifier.
|
||||
* Call type identifier.
|
||||
*/
|
||||
typedef enum {
|
||||
TypeAudio = 192,
|
||||
TypeVideo
|
||||
av_TypeAudio = 192,
|
||||
av_TypeVideo
|
||||
} ToxAvCallType;
|
||||
|
||||
|
||||
|
@ -84,41 +78,35 @@ typedef enum {
|
|||
} ToxAvCallState;
|
||||
|
||||
/**
|
||||
* @brief Error indicators.
|
||||
* Error indicators.
|
||||
*/
|
||||
typedef enum {
|
||||
ErrorNone = 0,
|
||||
ErrorInternal = -1, /* Internal error */
|
||||
ErrorAlreadyInCall = -2, /* Already has an active call */
|
||||
ErrorNoCall = -3, /* Trying to perform call action while not in a call */
|
||||
ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
|
||||
ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
|
||||
ErrorAudioPacketLost = -6, /* Indicating packet loss */
|
||||
ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
|
||||
ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
|
||||
ErrorTerminatingAudioRtp = -9, /* Returned in toxav_kill_transmission() */
|
||||
ErrorTerminatingVideoRtp = -10, /* Returned in toxav_kill_transmission() */
|
||||
ErrorPacketTooLarge = -11, /* Buffer exceeds size while encoding */
|
||||
ErrorInvalidCodecState = -12, /* Codec state not initialized */
|
||||
|
||||
av_ErrorNone = 0,
|
||||
av_ErrorInternal = -1, /* Internal error */
|
||||
av_ErrorAlreadyInCall = -2, /* Already has an active call */
|
||||
av_ErrorNoCall = -3, /* Trying to perform call action while not in a call */
|
||||
av_ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
|
||||
av_ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
|
||||
av_ErrorInvalidCodecState = -6, /* Codec state not initialized */
|
||||
av_ErrorPacketTooLarge = -7, /* Split packet exceeds it's limit */
|
||||
} ToxAvError;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Locally supported capabilities.
|
||||
* Locally supported capabilities.
|
||||
*/
|
||||
typedef enum {
|
||||
AudioEncoding = 1 << 0,
|
||||
AudioDecoding = 1 << 1,
|
||||
VideoEncoding = 1 << 2,
|
||||
VideoDecoding = 1 << 3
|
||||
av_AudioEncoding = 1 << 0,
|
||||
av_AudioDecoding = 1 << 1,
|
||||
av_VideoEncoding = 1 << 2,
|
||||
av_VideoDecoding = 1 << 3
|
||||
} ToxAvCapabilities;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Encoding settings.
|
||||
* Encoding settings.
|
||||
*/
|
||||
typedef struct _ToxAvCodecSettings {
|
||||
typedef struct _ToxAvCSettings {
|
||||
ToxAvCallType call_type;
|
||||
|
||||
uint32_t video_bitrate; /* In kbits/s */
|
||||
|
@ -132,256 +120,165 @@ typedef struct _ToxAvCodecSettings {
|
|||
} ToxAvCSettings;
|
||||
|
||||
extern const ToxAvCSettings av_DefaultSettings;
|
||||
extern const uint32_t av_jbufdc; /* Jitter buffer default capacity */
|
||||
extern const uint32_t av_VADd; /* VAD default treshold */
|
||||
|
||||
/**
|
||||
* @brief Start new A/V session. There can only be one session at the time. If you register more
|
||||
* it will result in undefined behaviour.
|
||||
*
|
||||
* @param messenger The messenger handle.
|
||||
* @param userdata The agent handling A/V session (i.e. phone).
|
||||
* @param video_width Width of video frame.
|
||||
* @param video_height Height of video frame.
|
||||
* @return ToxAv*
|
||||
* @retval NULL On error.
|
||||
* Start new A/V session. There can only be one session at the time.
|
||||
*/
|
||||
ToxAv *toxav_new(Tox *messenger, int32_t max_calls);
|
||||
|
||||
/**
|
||||
* @brief Remove A/V session.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @return void
|
||||
* Remove A/V session.
|
||||
*/
|
||||
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
|
||||
* Returns the interval in milliseconds when the next toxav_do() should be called.
|
||||
* If no call is active at the moment returns 200.
|
||||
*/
|
||||
void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata);
|
||||
uint32_t toxav_do_interval(ToxAv *av);
|
||||
|
||||
/**
|
||||
* @brief Register callback for receiving audio data
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param callback The callback
|
||||
* @return void
|
||||
* Main loop for the session. Best called right after tox_do();
|
||||
*/
|
||||
void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *),
|
||||
void *user_data);
|
||||
void toxav_do(ToxAv *av);
|
||||
|
||||
/**
|
||||
* @brief Register callback for receiving video data
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param callback The callback
|
||||
* @return void
|
||||
* Register callback for call state.
|
||||
*/
|
||||
void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *),
|
||||
void *user_data);
|
||||
void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Call user. Use its friend_id.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param user The user.
|
||||
* @param call_type Call type.
|
||||
* @param ringing_seconds Ringing timeout.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Register callback for audio data.
|
||||
*/
|
||||
int toxav_call(ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds);
|
||||
void toxav_register_audio_callback (ToxAvAudioCallback cb, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Hangup active call.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Register callback for video data.
|
||||
*/
|
||||
void toxav_register_video_callback (ToxAvVideoCallback cb, void *userdata);
|
||||
|
||||
/**
|
||||
* Call user. Use its friend_id.
|
||||
*/
|
||||
int toxav_call(ToxAv *av,
|
||||
int32_t *call_index,
|
||||
int friend_id,
|
||||
const ToxAvCSettings *csettings,
|
||||
int ringing_seconds);
|
||||
|
||||
/**
|
||||
* Hangup active call.
|
||||
*/
|
||||
int toxav_hangup(ToxAv *av, int32_t call_index);
|
||||
|
||||
/**
|
||||
* @brief Answer incomming call.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param call_type Answer with...
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Answer incoming call. Pass the csettings that you will use.
|
||||
*/
|
||||
int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings );
|
||||
|
||||
/**
|
||||
* @brief Reject incomming call.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param reason Optional reason. Set NULL if none.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Reject incoming call.
|
||||
*/
|
||||
int toxav_reject(ToxAv *av, int32_t call_index, const char *reason);
|
||||
|
||||
/**
|
||||
* @brief Cancel outgoing request.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param reason Optional reason.
|
||||
* @param peer_id peer friend_id
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Cancel outgoing request.
|
||||
*/
|
||||
int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason);
|
||||
|
||||
/**
|
||||
* @brief Notify peer that we are changing call settings
|
||||
*
|
||||
* @param av Handler.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Notify peer that we are changing codec settings.
|
||||
*/
|
||||
int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings);
|
||||
|
||||
/**
|
||||
* @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Terminate transmission. Note that transmission will be
|
||||
* terminated without informing remote peer. Usually called when we can't inform peer.
|
||||
*/
|
||||
int toxav_stop_call(ToxAv *av, int32_t call_index);
|
||||
|
||||
/**
|
||||
* @brief Must be call before any RTP transmission occurs.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param support_video Is video supported ? 1 : 0
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*.
|
||||
* Also, it must be called when call is started
|
||||
*/
|
||||
int toxav_prepare_transmission(ToxAv *av, int32_t call_index, uint32_t jbuf_size, uint32_t VAD_treshold,
|
||||
int support_video);
|
||||
int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video);
|
||||
|
||||
/**
|
||||
* @brief Call this at the end of the transmission.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Clears transmission data. Call this at the end of the transmission.
|
||||
*/
|
||||
int toxav_kill_transmission(ToxAv *av, int32_t call_index);
|
||||
|
||||
/**
|
||||
* @brief Encode and send video packet.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param frame The encoded frame.
|
||||
* @param frame_size The size of the encoded frame.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Encode video frame.
|
||||
*/
|
||||
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size);
|
||||
int toxav_prepare_video_frame ( ToxAv *av,
|
||||
int32_t call_index,
|
||||
uint8_t *dest,
|
||||
int dest_max,
|
||||
vpx_image_t *input);
|
||||
|
||||
/**
|
||||
* @brief Send audio frame.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param data The audio data encoded with toxav_prepare_audio_frame().
|
||||
* @param size Its size in number of bytes.
|
||||
* @return int
|
||||
* @retval 0 Success.
|
||||
* @retval ToxAvError On error.
|
||||
* Send encoded video packet.
|
||||
*/
|
||||
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size);
|
||||
|
||||
/**
|
||||
* Encode audio frame.
|
||||
*/
|
||||
int toxav_prepare_audio_frame ( ToxAv *av,
|
||||
int32_t call_index,
|
||||
uint8_t *dest,
|
||||
int dest_max,
|
||||
const int16_t *frame,
|
||||
int frame_size);
|
||||
|
||||
/**
|
||||
* Send encoded audio frame.
|
||||
*/
|
||||
int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size);
|
||||
|
||||
/**
|
||||
* @brief Encode video frame
|
||||
*
|
||||
* @param av Handler
|
||||
* @param dest Where to
|
||||
* @param dest_max Max size
|
||||
* @param input What to encode
|
||||
* @return int
|
||||
* @retval ToxAvError On error.
|
||||
* @retval >0 On success
|
||||
*/
|
||||
int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input );
|
||||
|
||||
/**
|
||||
* @brief Encode audio frame
|
||||
*
|
||||
* @param av Handler
|
||||
* @param dest dest
|
||||
* @param dest_max Max dest size
|
||||
* @param frame The frame
|
||||
* @param frame_size The frame size
|
||||
* @return int
|
||||
* @retval ToxAvError On error.
|
||||
* @retval >0 On success
|
||||
*/
|
||||
int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame,
|
||||
int frame_size);
|
||||
|
||||
/**
|
||||
* @brief Get peer transmission type. It can either be audio or video.
|
||||
*
|
||||
* @param av Handler.
|
||||
* @param peer The peer
|
||||
* @return int
|
||||
* @retval ToxAvCallType On success.
|
||||
* @retval ToxAvError On error.
|
||||
* Get codec settings from the peer. These were exchanged during call initialization
|
||||
* or when peer send us new csettings.
|
||||
*/
|
||||
int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest );
|
||||
|
||||
/**
|
||||
* @brief Get id of peer participating in conversation
|
||||
*
|
||||
* @param av Handler
|
||||
* @param peer peer index
|
||||
* @return int
|
||||
* @retval ToxAvError No peer id
|
||||
* Get friend id of peer participating in conversation.
|
||||
*/
|
||||
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer );
|
||||
|
||||
/**
|
||||
* @brief Get current call state
|
||||
*
|
||||
* @param av Handler
|
||||
* @param call_index What call
|
||||
* @return int
|
||||
* @retval ToxAvCallState State id
|
||||
* Get current call state.
|
||||
*/
|
||||
ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index );
|
||||
|
||||
/**
|
||||
* @brief Is certain capability supported
|
||||
*
|
||||
* @param av Handler
|
||||
* @return int
|
||||
* @retval 1 Yes.
|
||||
* @retval 0 No.
|
||||
* Is certain capability supported. Used to determine if encoding/decoding is ready.
|
||||
*/
|
||||
int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability );
|
||||
|
||||
/**
|
||||
* Returns tox reference.
|
||||
*/
|
||||
Tox *toxav_get_tox (ToxAv *av);
|
||||
|
||||
Tox *toxav_get_tox(ToxAv *av);
|
||||
/**
|
||||
* Set VAD activity treshold for calculating VAD. 40 is some middle value for treshold
|
||||
*/
|
||||
int toxav_set_vad_treshold (ToxAv *av, int32_t call_index, uint32_t treshold);
|
||||
|
||||
int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy );
|
||||
/**
|
||||
* Check if there is activity in the PCM data.
|
||||
* Activity is present if the calculated PCM energy is > ref_energy.
|
||||
* Returns bool.
|
||||
*/
|
||||
int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref);
|
||||
|
||||
/**
|
||||
* Returns number of active calls or -1 on error.
|
||||
*/
|
||||
int toxav_get_active_count (ToxAv *av);
|
||||
|
||||
/* Create a new toxav group.
|
||||
*
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
#include "util.h"
|
||||
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
|
||||
static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status);
|
||||
static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
|
||||
uint32_t length, uint8_t congestion_control);
|
||||
|
|
|
@ -80,7 +80,7 @@ const char *logger_stringify_level(LoggerLevel level)
|
|||
static const char *strings [] = {
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
"WARNING",
|
||||
"WARN",
|
||||
"ERROR"
|
||||
};
|
||||
|
||||
|
@ -143,11 +143,26 @@ void logger_write (LoggerLevel level, const char *format, ...)
|
|||
|
||||
char *logger_timestr(char *dest, size_t max_size)
|
||||
{
|
||||
uint64_t diff = (current_time_monotonic() - logger.start_time); /* ms */
|
||||
time_t timer;
|
||||
struct tm *tm_info;
|
||||
|
||||
time(&timer);
|
||||
tm_info = localtime(&timer);
|
||||
|
||||
strftime(dest, max_size, "%m:%d %H:%M:%S", tm_info);
|
||||
|
||||
return dest;
|
||||
|
||||
/*uint64_t diff = (current_time_monotonic() - logger.start_time); /* ms * /
|
||||
snprintf(dest, max_size, "%"PRIu64"", diff);
|
||||
|
||||
return dest; */
|
||||
}
|
||||
|
||||
char *logger_posstr (char *dest, size_t max_size, const char *file, int line)
|
||||
{
|
||||
snprintf(dest, max_size, "%s:%d", file, line);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
#endif /* LOGGING */
|
||||
|
|
|
@ -44,6 +44,7 @@ const char *logger_stringify_level(LoggerLevel level);
|
|||
unsigned logger_get_pid();
|
||||
void logger_write (LoggerLevel level, const char *format, ...);
|
||||
char *logger_timestr (char *dest, size_t max_size);
|
||||
char *logger_posstr (char *dest, size_t max_size, const char *file, int line);
|
||||
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
|
||||
#define _SFILE (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
|
@ -51,16 +52,19 @@ char *logger_timestr (char *dest, size_t max_size);
|
|||
#define _SFILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#endif
|
||||
|
||||
#define WRITE_FORMAT(__LEVEL__, format) char __time__[20]; char* the_str = calloc(sizeof(char), strlen(format)+ 500); sprintf(the_str, "\n[%u] [%s] [%s] [%s:%d %s()] %s", \
|
||||
logger_get_pid(), logger_stringify_level(__LEVEL__), logger_timestr(__time__, 20), _SFILE, __LINE__, __func__, format)
|
||||
#define LFORMAT "\n%-15s %-7u %-5s %-20s - %s"
|
||||
#define WRITE_FORMAT(__LEVEL__, __WHAT__) \
|
||||
char __time__[15]; char posstr[200]; char the_str [4096]; \
|
||||
snprintf(the_str, 4096, LFORMAT, logger_timestr(__time__, 15), logger_get_pid(), \
|
||||
logger_stringify_level(__LEVEL__), logger_posstr(posstr, 200, _SFILE, __LINE__), __WHAT__)
|
||||
|
||||
/* Use these macros */
|
||||
|
||||
#define LOGGER_INIT(name, level) logger_init(name, level);
|
||||
#define LOGGER_INFO(format, ...) do { WRITE_FORMAT(INFO, format); logger_write( INFO, the_str, ##__VA_ARGS__ ); free(the_str); } while (0)
|
||||
#define LOGGER_DEBUG(format, ...) do { WRITE_FORMAT(DEBUG, format); logger_write( DEBUG, the_str, ##__VA_ARGS__ ); free(the_str); } while (0)
|
||||
#define LOGGER_WARNING(format, ...) do { WRITE_FORMAT(WARNING, format); logger_write( WARNING, the_str, ##__VA_ARGS__ ); free(the_str); } while (0)
|
||||
#define LOGGER_ERROR(format, ...) do { WRITE_FORMAT(ERROR, format); logger_write( ERROR, the_str, ##__VA_ARGS__ ); free(the_str); } while (0)
|
||||
#define LOGGER_INFO(format, ...) do { WRITE_FORMAT(INFO, format); logger_write( INFO, the_str, ##__VA_ARGS__ ); } while (0)
|
||||
#define LOGGER_DEBUG(format, ...) do { WRITE_FORMAT(DEBUG, format); logger_write( DEBUG, the_str, ##__VA_ARGS__ ); } while (0)
|
||||
#define LOGGER_WARNING(format, ...) do { WRITE_FORMAT(WARNING, format); logger_write( WARNING, the_str, ##__VA_ARGS__ ); } while (0)
|
||||
#define LOGGER_ERROR(format, ...) do { WRITE_FORMAT(ERROR, format); logger_write( ERROR, the_str, ##__VA_ARGS__ ); } while (0)
|
||||
|
||||
/* To do some checks or similar only when logging use this */
|
||||
#define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define inline__ inline __attribute__((always_inline))
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
void unix_time_update();
|
||||
uint64_t unix_time();
|
||||
|
|
Loading…
Reference in New Issue
Block a user