av refactor

This commit is contained in:
mannol 2014-11-18 00:46:46 +01:00
parent 4e6f993e7d
commit 386c9748d4
14 changed files with 1571 additions and 2305 deletions

View File

@ -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;
@ -65,37 +67,30 @@ 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;
/* Bob always receives invite */
cast->Bob.status = Ringing;
cast->Bob.call_index = call_index;
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;
/* 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;
if (cast->Alice.av == av)
{
/* Alice always sends invite */
cast->Alice.status = Ringing;
}
else if (cast->Bob.av == av)
{
// ...
}
}
@ -104,17 +99,32 @@ 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(av, call_index, av_jbufdc, av_VADd, 1);
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, 1);
}
}
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;
if (cast->Alice.av == av)
{
// ...
}
else if (cast->Bob.av == av)
{
printf ( "Call Canceled for Bob!\n" );
cast->Bob.status = Canceled;
}
}
void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
{
@ -123,55 +133,101 @@ 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 */
cast->Alice.status = Rejected;
if (cast->Alice.av == av)
{
cast->Alice.status = Rejected;
}
else if (cast->Bob.av == av)
{
//... ignor
}
}
void callback_call_ended ( void *av, int32_t call_index, void *_arg )
{
Status *cast = _arg;
printf ( "Call ended for Bob!\n" );
cast->Bob.status = Ended;
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("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
);
}
printf("New settings: \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"
"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_requ_timeout ( void *av, int32_t call_index, void *_arg )
{
Status *cast = _arg;
printf("Call timed-out!\n");
cast->Alice.status = TimedOut;
if (cast->Alice.av == av)
{
cast->Alice.status = TimedOut;
}
else if (cast->Bob.av == av)
{
cast->Bob.status = TimedOut;
}
}
static void callback_audio(ToxAv *av, int32_t call_index, int16_t *data, int length, void *userdata)
{
}
void callback_audio (void* agent, int32_t call_idx, const int16_t* PCM, uint16_t size, void* data)
{}
static void callback_video(ToxAv *av, int32_t call_index, vpx_image_t *img, void *userdata)
{
}
void callback_video (void* agent, int32_t call_idx, const vpx_image_t* img, void* data)
{}
void register_callbacks(ToxAv *av, void *data)
{
@ -180,17 +236,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 +253,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;\
@ -495,7 +547,11 @@ START_TEST(test_AV_flows)
tox_do(bootstrap_node);
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 +570,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;
}
@ -536,7 +592,10 @@ START_TEST(test_AV_flows)
tox_do(bootstrap_node);
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");

View File

@ -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,49 @@ 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;*/
Status *cast = _arg;
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;
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_recv_ending ( void *av, int32_t call_index, void *_arg )
{
Status *cast = _arg;
cast->calls[call_index].Caller.status = Ended;
}
void callback_call_started ( void *av, int32_t call_index, void *_arg )
{
/*
Status *cast = _arg;
cast->calls[call_index].Callee.status = InCall;*/
Status *cast = _arg;
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 +120,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 +174,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 +234,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 +247,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 +262,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;
@ -349,6 +330,12 @@ void test_AV_three_calls()
tox_do(callees[0]);
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 +353,7 @@ void test_AV_three_calls()
tox_kill(callees[i]);
}
// END_TEST
END_TEST
@ -382,21 +369,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;
}

View File

@ -32,33 +32,124 @@
#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;
while (size <= capacity) {
size *= 2;
}
JitterBuffer *q;
if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL;
if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
free(q);
return NULL;
}
q->size = size;
q->capacity = 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,48 +159,50 @@ 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;
}
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;
return NULL;
}
unsigned int num = q->bottom % q->size;
if (q->queue[num]) {
RTPMessage *ret = q->queue[num];
q->queue[num] = NULL;
@ -117,19 +210,18 @@ RTPMessage *dequeue(JitterBuffer *q, int *success)
*success = 1;
return ret;
}
if ((uint32_t)(q->top - q->bottom) > q->capacity) {
++q->bottom;
*success = 2;
return NULL;
}
*success = 0;
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 +233,228 @@ 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 +477,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,149 +496,143 @@ 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;
}
retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ?
audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration;
return retu;
if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
LOGGER_WARNING("Jitter buffer creaton failed!");
goto error;
}
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;
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);
if ( cs->audio_decoder )
opus_decoder_destroy(cs->audio_decoder);
if ( cs->capabilities & v_decoding )
vpx_codec_destroy(&cs->v_decoder);
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 +648,98 @@ 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);
}
}

View File

@ -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,85 +35,126 @@
#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 _CodecState {
typedef struct _CSSession {
/* VIDEO
*
*
*/
int support_video;
/* video encoding */
vpx_codec_ctx_t v_encoder;
uint32_t frame_counter;
/* 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;
/* OTHER
*
*
*/
uint64_t capabilities; /* supports*/
/* Buffering */
void* abuf_raw, *vbuf_raw; /* Un-decoded data */
_Bool active;
pthread_mutex_t queue_mutex[1];
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);
typedef struct _JitterBuffer {
RTPMessage **queue;
uint32_t size;
uint32_t capacity;
uint16_t bottom;
uint16_t top;
} JitterBuffer;
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);
JitterBuffer *create_queue(unsigned int capacity);
void terminate_queue(JitterBuffer *q);
void queue(JitterBuffer *q, RTPMessage *pk);
RTPMessage *dequeue(JitterBuffer *q, int *success);
/**
* 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);
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 );
/* 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);
void codec_terminate_session(CodecState *cs);
/* 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_ */

File diff suppressed because it is too large Load Diff

View File

@ -31,9 +31,8 @@ typedef uint8_t MSICallIDType[12];
typedef uint8_t MSIReasonStrType[255];
typedef 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,16 +96,15 @@ typedef struct _MSICallbackCont {
} MSICallbackCont;
/**
* @brief The call struct.
*
* The call struct.
*/
typedef struct _MSICall { /* Call info structure */
struct _MSISession *session; /* Session pointer */
MSICallState state;
MSICSettings csettings_local; /* Local call settings */
MSICSettings *csettings_peer; /* Peers call settings */
MSICSettings csettings_local; /* Local call settings */
MSICSettings *csettings_peer; /* Peers call settings */
MSICallIDType id; /* Random value identifying the call */
@ -120,8 +113,6 @@ typedef struct _MSICall { /* Call info structure */
int request_timer_id; /* Timer id for outgoing request/action */
int 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 {
@ -143,125 +133,71 @@ typedef struct _MSISession {
Messenger *messenger_handle;
uint32_t frequ;
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
pthread_mutex_t mutex;
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 */

View File

@ -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;

View File

@ -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,11 +73,11 @@ typedef struct _RTPMessage {
/**
* @brief 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
* session parameters.
* 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
* session parameters.
*
*/
typedef struct _RTPSession {
@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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.
*

View File

@ -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);

View File

@ -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 */

View File

@ -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)

View File

@ -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();