mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Merge remote-tracking branch 'upstream/master' into rm-files
This commit is contained in:
commit
e1ad6cc8f9
|
@ -78,7 +78,7 @@ Note, if you install from ports select NaCl for performance, and sodium if you w
|
|||
|
||||
You should get and install [libsodium](https://github.com/jedisct1/libsodium). If you have installed `libsodium` from repo, ommit this step, and jump directly to [compiling toxcore](#compile-toxcore):
|
||||
```bash
|
||||
git clone git://github.com/jedisct1/libsodium.git
|
||||
git clone https://github.com/jedisct1/libsodium.git
|
||||
cd libsodium
|
||||
git checkout tags/1.0.3
|
||||
./autogen.sh
|
||||
|
@ -93,7 +93,7 @@ Or if checkinstall is not easily available for your distribution (e.g., Fedora),
|
|||
this will install the libs to /usr/local/lib and the headers to /usr/local/include:
|
||||
|
||||
```bash
|
||||
git clone git://github.com/jedisct1/libsodium.git
|
||||
git clone https://github.com/jedisct1/libsodium.git
|
||||
cd libsodium
|
||||
git checkout tags/1.0.3
|
||||
./autogen.sh
|
||||
|
@ -121,7 +121,7 @@ sudo ldconfig
|
|||
|
||||
Then clone this repo, generate makefile, and install `toxcore` system-wide:
|
||||
```bash
|
||||
git clone git://github.com/irungentoo/toxcore.git
|
||||
git clone https://github.com/irungentoo/toxcore.git
|
||||
cd toxcore
|
||||
autoreconf -i
|
||||
./configure
|
||||
|
@ -153,7 +153,7 @@ brew install libtool automake autoconf libsodium check
|
|||
```
|
||||
Then clone this repo and generate makefile:
|
||||
```bash
|
||||
git clone git://github.com/irungentoo/toxcore.git
|
||||
git clone https://github.com/irungentoo/toxcore.git
|
||||
cd toxcore
|
||||
autoreconf -i
|
||||
./configure
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
![Project Tox](https://raw.github.com/irungentoo/toxcore/master/other/tox.png "Project Tox")
|
||||
***
|
||||
|
||||
With the rise of governmental monitoring programs, Tox, a FOSS initiative, aims to be an easy to use, all-in-one communication platform that ensures their users full privacy and secure message delivery.<br /> <br />
|
||||
With the rise of government surveillance programs, Tox, a FOSS initiative, aims to be an easy to use, all-in-one communication platform that ensures full privacy and secure message delivery.<br /> <br />
|
||||
|
||||
[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://wiki.tox.chat/Binaries) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md) **|** **IRC Channel:** [#tox@freenode](https://webchat.freenode.net/?channels=tox)
|
||||
[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://wiki.tox.chat/Binaries) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md)
|
||||
|
||||
**IRC Channels:** [#tox@freenode](https://webchat.freenode.net/?channels=tox), [#tox-dev@freenode](https://webchat.freenode.net/?channels=tox-dev)
|
||||
|
||||
|
||||
## The Complex Stuff:
|
||||
|
@ -36,4 +38,3 @@ The goal of this project is to create a configuration-free P2P Skype replacement
|
|||
- [Compiling](/INSTALL.md)
|
||||
- [DHT Protocol](/docs/updates/DHT.md)<br />
|
||||
- [Crypto](/docs/updates/Crypto.md)<br />
|
||||
|
||||
|
|
|
@ -153,7 +153,8 @@ void test_addto_lists_bad(DHT *dht,
|
|||
{
|
||||
// check "bad" clients replacement
|
||||
int used, test1, test2, test3;
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES],
|
||||
test_id3[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0;
|
||||
|
||||
randombytes(public_key, sizeof(public_key));
|
||||
|
@ -196,7 +197,8 @@ void test_addto_lists_possible_bad(DHT *dht,
|
|||
{
|
||||
// check "possibly bad" clients replacement
|
||||
int used, test1, test2, test3;
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES],
|
||||
test_id3[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0;
|
||||
|
||||
randombytes(public_key, sizeof(public_key));
|
||||
|
|
|
@ -135,7 +135,7 @@ START_TEST(test_m_addfriend)
|
|||
if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, really_bad_len) != FAERR_TOOLONG)
|
||||
ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", really_bad_len);
|
||||
*/
|
||||
/* this will error if the original m_addfriend_norequest() failed */
|
||||
/* this will return an error if the original m_addfriend_norequest() failed */
|
||||
/* if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, good_len) != FAERR_ALREADYSENT)
|
||||
ck_abort_msg("m_addfriend did NOT catch adding a friend we already have.\n"
|
||||
"(this can be caused by the error of m_addfriend_norequest in"
|
||||
|
@ -144,7 +144,7 @@ START_TEST(test_m_addfriend)
|
|||
if(m_addfriend(m, (uint8_t *)good_id_b, (uint8_t *)bad_data, bad_len) != FAERR_NOMESSAGE)
|
||||
ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", bad_len);
|
||||
*/
|
||||
/* this should REALLY error */
|
||||
/* this should REALLY return an error */
|
||||
/*
|
||||
* TODO: validate client_id in m_addfriend?
|
||||
if(m_addfriend((uint8_t *)bad_id, (uint8_t *)good_data, good_len) >= 0)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,18 +2,27 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBCHECK
|
||||
# include <assert.h>
|
||||
|
||||
# define ck_assert(X) assert(X);
|
||||
# define START_TEST(NAME) void NAME ()
|
||||
# define END_TEST
|
||||
#else
|
||||
# include "helpers.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/crypto_core.h"
|
||||
#include "../toxav/toxav.h"
|
||||
|
@ -26,365 +35,321 @@
|
|||
#define c_sleep(x) usleep(1000*x)
|
||||
#endif
|
||||
|
||||
pthread_mutex_t muhmutex;
|
||||
|
||||
typedef enum _CallStatus {
|
||||
none,
|
||||
InCall,
|
||||
Ringing,
|
||||
Ended,
|
||||
Rejected,
|
||||
Canceled
|
||||
typedef struct {
|
||||
bool incoming;
|
||||
uint32_t state;
|
||||
} CallControl;
|
||||
|
||||
} CallStatus;
|
||||
typedef struct {
|
||||
ToxAV *AliceAV;
|
||||
ToxAV *BobAV;
|
||||
CallControl *AliceCC;
|
||||
CallControl *BobCC;
|
||||
uint32_t friend_number;
|
||||
} thread_data;
|
||||
|
||||
typedef struct _Party {
|
||||
CallStatus status;
|
||||
ToxAv *av;
|
||||
int id;
|
||||
} Party;
|
||||
|
||||
typedef struct _ACall {
|
||||
pthread_t tid;
|
||||
int idx;
|
||||
|
||||
Party Caller;
|
||||
Party Callee;
|
||||
} ACall;
|
||||
|
||||
typedef struct _Status {
|
||||
ACall calls[3]; /* Make 3 calls for this test */
|
||||
} Status;
|
||||
|
||||
Status status_control;
|
||||
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
/**
|
||||
* Callbacks
|
||||
*/
|
||||
void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
|
||||
{
|
||||
(void) av;
|
||||
(void) audio_enabled;
|
||||
(void) video_enabled;
|
||||
|
||||
printf("Handling CALL callback\n");
|
||||
((CallControl *)user_data)[friend_number].incoming = true;
|
||||
}
|
||||
void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
|
||||
{
|
||||
printf("Handling CALL STATE callback: %d %p\n", state, av);
|
||||
((CallControl *)user_data)[friend_number].state = state;
|
||||
}
|
||||
void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
uint16_t width, uint16_t height,
|
||||
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||
void *user_data)
|
||||
{
|
||||
(void) av;
|
||||
(void) friend_number;
|
||||
(void) width;
|
||||
(void) height;
|
||||
(void) y;
|
||||
(void) u;
|
||||
(void) v;
|
||||
(void) ystride;
|
||||
(void) ustride;
|
||||
(void) vstride;
|
||||
(void) user_data;
|
||||
}
|
||||
void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
int16_t const *pcm,
|
||||
size_t sample_count,
|
||||
uint8_t channels,
|
||||
uint32_t sampling_rate,
|
||||
void *user_data)
|
||||
{
|
||||
(void) av;
|
||||
(void) friend_number;
|
||||
(void) pcm;
|
||||
(void) sample_count;
|
||||
(void) channels;
|
||||
(void) sampling_rate;
|
||||
(void) user_data;
|
||||
}
|
||||
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
|
||||
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
|
||||
/**
|
||||
* Iterate helper
|
||||
*/
|
||||
ToxAV *setup_av_instance(Tox *tox, CallControl *CC)
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Callee.status = Ringing;
|
||||
TOXAV_ERR_NEW error;
|
||||
|
||||
ToxAV *av = toxav_new(tox, &error);
|
||||
ck_assert(error == TOXAV_ERR_NEW_OK);
|
||||
|
||||
toxav_callback_call(av, t_toxav_call_cb, CC);
|
||||
toxav_callback_call_state(av, t_toxav_call_state_cb, CC);
|
||||
toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC);
|
||||
toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC);
|
||||
|
||||
return av;
|
||||
}
|
||||
void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
|
||||
void *call_thread(void *pd)
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Caller.status = Ringing;
|
||||
}
|
||||
void callback_call_ended ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
|
||||
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;
|
||||
|
||||
if (av == cast->calls[call_index].Caller.av)
|
||||
cast->calls[call_index].Caller.status = InCall;
|
||||
else
|
||||
cast->calls[call_index].Callee.status = InCall;
|
||||
}
|
||||
void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
}
|
||||
void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
Status *cast = _arg;
|
||||
cast->calls[call_index].Caller.status = Rejected;
|
||||
}
|
||||
|
||||
void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
|
||||
{
|
||||
ck_assert_msg(0, "No answer!");
|
||||
}
|
||||
|
||||
void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
|
||||
{}
|
||||
|
||||
void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
|
||||
{}
|
||||
|
||||
void register_callbacks(ToxAv *av, void *data)
|
||||
{
|
||||
toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data);
|
||||
toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data);
|
||||
toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
|
||||
toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data);
|
||||
toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data);
|
||||
toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data);
|
||||
toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
|
||||
ToxAV *AliceAV = ((thread_data *) pd)->AliceAV;
|
||||
ToxAV *BobAV = ((thread_data *) pd)->BobAV;
|
||||
CallControl *AliceCC = ((thread_data *) pd)->AliceCC;
|
||||
CallControl *BobCC = ((thread_data *) pd)->BobCC;
|
||||
uint32_t friend_number = ((thread_data *) pd)->friend_number;
|
||||
|
||||
|
||||
toxav_register_audio_callback(av, callback_audio, NULL);
|
||||
toxav_register_video_callback(av, callback_video, NULL);
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
memset(AliceCC, 0, sizeof(CallControl));
|
||||
memset(BobCC, 0, sizeof(CallControl));
|
||||
|
||||
int call_running[3];
|
||||
|
||||
void *in_thread_call (void *arg)
|
||||
{
|
||||
#define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args)
|
||||
|
||||
ACall *this_call = arg;
|
||||
uint64_t start = 0;
|
||||
int step = 0;
|
||||
int call_idx;
|
||||
|
||||
const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000);
|
||||
int16_t sample_payload[frame_size];
|
||||
randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size);
|
||||
|
||||
uint8_t prepared_payload[RTP_PAYLOAD_SIZE];
|
||||
|
||||
register_callbacks(this_call->Caller.av, &status_control);
|
||||
register_callbacks(this_call->Callee.av, arg);
|
||||
|
||||
/* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
while (call_running[this_call->idx]) {
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
|
||||
switch ( step ) {
|
||||
case 0: /* CALLER */
|
||||
toxav_call(this_call->Caller.av, &call_idx, this_call->Callee.id, &av_DefaultSettings, 10);
|
||||
call_print(call_idx, "Calling ...");
|
||||
step++;
|
||||
break;
|
||||
|
||||
case 1: /* CALLEE */
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
if (this_call->Caller.status == Ringing) {
|
||||
call_print(call_idx, "Callee answers ...");
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
toxav_answer(this_call->Callee.av, 0, &av_DefaultSettings);
|
||||
step++;
|
||||
start = time(NULL);
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
break;
|
||||
|
||||
case 2: /* Rtp transmission */
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
if (this_call->Caller.status == InCall) { /* I think this is okay */
|
||||
call_print(call_idx, "Sending rtp ...");
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
|
||||
c_sleep(1000); /* We have race condition here */
|
||||
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" );
|
||||
}
|
||||
|
||||
|
||||
while (time(NULL) - start < 10) { /* 10 seconds */
|
||||
/* Both send */
|
||||
toxav_send_audio(this_call->Caller.av, call_idx, prepared_payload, payload_size);
|
||||
|
||||
toxav_send_audio(this_call->Callee.av, 0, prepared_payload, payload_size);
|
||||
|
||||
/* Both receive */
|
||||
int16_t storage[RTP_PAYLOAD_SIZE];
|
||||
int recved;
|
||||
|
||||
c_sleep(20);
|
||||
}
|
||||
|
||||
step++; /* This terminates the loop */
|
||||
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
toxav_kill_transmission(this_call->Callee.av, 0);
|
||||
toxav_kill_transmission(this_call->Caller.av, call_idx);
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
|
||||
/* Call over CALLER hangs up */
|
||||
toxav_hangup(this_call->Caller.av, call_idx);
|
||||
call_print(call_idx, "Hanging up ...");
|
||||
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
break;
|
||||
|
||||
case 3: /* Wait for Both to have status ended */
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
if (this_call->Caller.status == Ended) {
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
c_sleep(1000); /* race condition */
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
this_call->Callee.status = Ended;
|
||||
call_running[this_call->idx] = 0;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
|
||||
break;
|
||||
{ /* Call */
|
||||
TOXAV_ERR_CALL rc;
|
||||
toxav_call(AliceAV, friend_number, 48, 3000, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_OK) {
|
||||
printf("toxav_call failed: %d\n", rc);
|
||||
ck_assert(0);
|
||||
}
|
||||
|
||||
c_sleep(20);
|
||||
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&muhmutex);
|
||||
call_print(call_idx, "Call ended successfully!");
|
||||
while (!BobCC->incoming)
|
||||
c_sleep(10);
|
||||
|
||||
{ /* Answer */
|
||||
TOXAV_ERR_ANSWER rc;
|
||||
toxav_answer(BobAV, 0, 8, 500, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_ANSWER_OK) {
|
||||
printf("toxav_answer failed: %d\n", rc);
|
||||
ck_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
c_sleep(30);
|
||||
|
||||
int16_t PCM[960];
|
||||
uint8_t video_y[800 * 600];
|
||||
uint8_t video_u[800 * 600 / 2];
|
||||
uint8_t video_v[800 * 600 / 2];
|
||||
|
||||
memset(PCM, 0, sizeof(PCM));
|
||||
memset(video_y, 0, sizeof(video_y));
|
||||
memset(video_u, 0, sizeof(video_u));
|
||||
memset(video_v, 0, sizeof(video_v));
|
||||
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
while (time(NULL) - start_time < 4) {
|
||||
toxav_iterate(AliceAV);
|
||||
toxav_iterate(BobAV);
|
||||
|
||||
toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL);
|
||||
toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL);
|
||||
|
||||
toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL);
|
||||
toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL);
|
||||
|
||||
c_sleep(10);
|
||||
}
|
||||
|
||||
{ /* Hangup */
|
||||
TOXAV_ERR_CALL_CONTROL rc;
|
||||
toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
|
||||
printf("toxav_call_control failed: %d %p %p\n", rc, AliceAV, BobAV);
|
||||
}
|
||||
}
|
||||
|
||||
c_sleep(30);
|
||||
|
||||
printf ("Closing thread\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
START_TEST(test_AV_three_calls)
|
||||
// void test_AV_three_calls()
|
||||
{
|
||||
Tox *Alice, *bootstrap, *Bobs[3];
|
||||
ToxAV *AliceAV, *BobsAV[3];
|
||||
|
||||
CallControl AliceCC[3], BobsCC[3];
|
||||
|
||||
{
|
||||
TOX_ERR_NEW error;
|
||||
|
||||
bootstrap = tox_new(NULL, &error);
|
||||
ck_assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Alice = tox_new(NULL, &error);
|
||||
ck_assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Bobs[0] = tox_new(NULL, &error);
|
||||
ck_assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Bobs[1] = tox_new(NULL, &error);
|
||||
ck_assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Bobs[2] = tox_new(NULL, &error);
|
||||
ck_assert(error == TOX_ERR_NEW_OK);
|
||||
}
|
||||
|
||||
printf("Created 5 instances of Tox\n");
|
||||
printf("Preparing network...\n");
|
||||
long long unsigned int cur_time = time(NULL);
|
||||
Tox *bootstrap_node = tox_new(0, 0);
|
||||
Tox *caller = tox_new(0, 0);
|
||||
Tox *callees[3] = {
|
||||
tox_new(0, 0),
|
||||
tox_new(0, 0),
|
||||
tox_new(0, 0),
|
||||
};
|
||||
|
||||
uint32_t to_compare = 974536;
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
|
||||
tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
|
||||
tox_self_get_address(Alice, address);
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
for ( i = 0; i < 3; i ++ ) {
|
||||
uint32_t to_compare = 974536;
|
||||
tox_callback_friend_request(callees[i], accept_friend_request, &to_compare);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(callees[i], address);
|
||||
|
||||
uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0);
|
||||
ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
|
||||
}
|
||||
ck_assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
|
||||
ck_assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
|
||||
ck_assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(caller);
|
||||
tox_iterate(bootstrap);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bobs[0]);
|
||||
tox_iterate(Bobs[1]);
|
||||
tox_iterate(Bobs[2]);
|
||||
|
||||
for (i = 0; i < 3; i ++) {
|
||||
tox_iterate(callees[i]);
|
||||
}
|
||||
|
||||
|
||||
if (tox_self_get_connection_status(bootstrap_node) &&
|
||||
tox_self_get_connection_status(caller) &&
|
||||
tox_self_get_connection_status(callees[0]) &&
|
||||
tox_self_get_connection_status(callees[1]) &&
|
||||
tox_self_get_connection_status(callees[2]) && off) {
|
||||
if (tox_self_get_connection_status(bootstrap) &&
|
||||
tox_self_get_connection_status(Alice) &&
|
||||
tox_self_get_connection_status(Bobs[0]) &&
|
||||
tox_self_get_connection_status(Bobs[1]) &&
|
||||
tox_self_get_connection_status(Bobs[2]) && off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
|
||||
if (tox_friend_get_connection_status(caller, 0, 0) &&
|
||||
tox_friend_get_connection_status(caller, 1, 0) &&
|
||||
tox_friend_get_connection_status(caller, 2, 0) )
|
||||
if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP)
|
||||
break;
|
||||
|
||||
c_sleep(20);
|
||||
}
|
||||
|
||||
printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time);
|
||||
AliceAV = setup_av_instance(Alice, AliceCC);
|
||||
BobsAV[0] = setup_av_instance(Bobs[0], BobsCC + 0);
|
||||
BobsAV[1] = setup_av_instance(Bobs[1], BobsCC + 1);
|
||||
BobsAV[2] = setup_av_instance(Bobs[2], BobsCC + 2);
|
||||
|
||||
ToxAv *uniqcallerav = toxav_new(caller, 3);
|
||||
printf("Created 4 instances of ToxAV\n");
|
||||
printf("All set after %llu seconds!\n", time(NULL) - cur_time);
|
||||
|
||||
for (i = 0; i < 3; i ++) {
|
||||
status_control.calls[i].idx = i;
|
||||
thread_data tds[3];
|
||||
tds[0].AliceAV = AliceAV;
|
||||
tds[0].BobAV = BobsAV[0];
|
||||
tds[0].AliceCC = AliceCC + 0;
|
||||
tds[0].BobCC = BobsCC + 0;
|
||||
tds[0].friend_number = 0;
|
||||
|
||||
status_control.calls[i].Caller.av = uniqcallerav;
|
||||
status_control.calls[i].Caller.id = 0;
|
||||
status_control.calls[i].Caller.status = none;
|
||||
tds[1].AliceAV = AliceAV;
|
||||
tds[1].BobAV = BobsAV[1];
|
||||
tds[1].AliceCC = AliceCC + 1;
|
||||
tds[1].BobCC = BobsCC + 1;
|
||||
tds[1].friend_number = 1;
|
||||
|
||||
status_control.calls[i].Callee.av = toxav_new(callees[i], 1);
|
||||
status_control.calls[i].Callee.id = i;
|
||||
status_control.calls[i].Callee.status = none;
|
||||
}
|
||||
tds[2].AliceAV = AliceAV;
|
||||
tds[2].BobAV = BobsAV[2];
|
||||
tds[2].AliceCC = AliceCC + 2;
|
||||
tds[2].BobCC = BobsCC + 2;
|
||||
tds[2].friend_number = 2;
|
||||
|
||||
pthread_mutex_init(&muhmutex, NULL);
|
||||
pthread_t tids[3];
|
||||
(void) pthread_create(tids + 0, NULL, call_thread, tds + 0);
|
||||
(void) pthread_create(tids + 1, NULL, call_thread, tds + 1);
|
||||
(void) pthread_create(tids + 2, NULL, call_thread, tds + 2);
|
||||
|
||||
for ( i = 0; i < 3; i++ ) {
|
||||
call_running[i] = 1;
|
||||
pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]);
|
||||
}
|
||||
(void) pthread_detach(tids[0]);
|
||||
(void) pthread_detach(tids[1]);
|
||||
(void) pthread_detach(tids[2]);
|
||||
|
||||
/* Now start 3 calls and they'll run for 10 s */
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
for ( i = 0; i < 3; i++ )
|
||||
pthread_detach(status_control.calls[i].tid);
|
||||
|
||||
while (call_running[0] || call_running[1] || call_running[2]) {
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(caller);
|
||||
tox_iterate(callees[0]);
|
||||
tox_iterate(callees[1]);
|
||||
tox_iterate(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);
|
||||
while (time(NULL) - start_time < 5) {
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bobs[0]);
|
||||
tox_iterate(Bobs[1]);
|
||||
tox_iterate(Bobs[2]);
|
||||
c_sleep(20);
|
||||
}
|
||||
|
||||
toxav_kill(status_control.calls[0].Caller.av);
|
||||
toxav_kill(status_control.calls[0].Callee.av);
|
||||
toxav_kill(status_control.calls[1].Callee.av);
|
||||
toxav_kill(status_control.calls[2].Callee.av);
|
||||
(void) pthread_join(tids[0], NULL);
|
||||
(void) pthread_join(tids[1], NULL);
|
||||
(void) pthread_join(tids[2], NULL);
|
||||
|
||||
tox_kill(bootstrap_node);
|
||||
tox_kill(caller);
|
||||
|
||||
for ( i = 0; i < 3; i ++)
|
||||
tox_kill(callees[i]);
|
||||
printf ("Killing all instances\n");
|
||||
toxav_kill(BobsAV[0]);
|
||||
toxav_kill(BobsAV[1]);
|
||||
toxav_kill(BobsAV[2]);
|
||||
toxav_kill(AliceAV);
|
||||
tox_kill(Bobs[0]);
|
||||
tox_kill(Bobs[1]);
|
||||
tox_kill(Bobs[2]);
|
||||
tox_kill(Alice);
|
||||
tox_kill(bootstrap);
|
||||
|
||||
printf("\nTest successful!\n");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
#ifndef HAVE_LIBCHECK
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
|
||||
test_AV_three_calls();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
Suite *tox_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("ToxAV");
|
||||
|
@ -399,6 +364,9 @@ Suite *tox_suite(void)
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
Suite *tox = tox_suite();
|
||||
SRunner *test_runner = srunner_create(tox);
|
||||
|
||||
|
@ -410,8 +378,5 @@ int main(int argc, char *argv[])
|
|||
srunner_free(test_runner);
|
||||
|
||||
return number_failed;
|
||||
|
||||
// test_AV_three_calls();
|
||||
|
||||
// return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -33,7 +33,7 @@ BUILD_TESTS="yes"
|
|||
BUILD_AV="yes"
|
||||
BUILD_TESTING="yes"
|
||||
|
||||
LOGGING="no"
|
||||
TOX_LOGGER="no"
|
||||
LOGGING_OUTNAM="libtoxcore.log"
|
||||
|
||||
NCURSES_FOUND="no"
|
||||
|
@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir],
|
|||
]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([log],
|
||||
[AC_HELP_STRING([--enable-log], [enable logging (default: auto)]) ],
|
||||
AC_ARG_ENABLE([logging],
|
||||
[AC_HELP_STRING([--enable-logging], [enable logging (default: auto)]) ],
|
||||
[
|
||||
if test "x$enableval" = "xyes"; then
|
||||
LOGGING="yes"
|
||||
TOX_LOGGER="yes"
|
||||
|
||||
AC_DEFINE([LOGGING], [], [If logging enabled])
|
||||
AC_DEFINE([TOX_LOGGER], [], [If logging enabled])
|
||||
AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value])
|
||||
AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger])
|
||||
fi
|
||||
|
@ -99,7 +99,7 @@ AC_ARG_WITH(log-level,
|
|||
AC_HELP_STRING([--with-log-level=LEVEL],
|
||||
[Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]),
|
||||
[
|
||||
if test "x$LOGGING" = "xno"; then
|
||||
if test "x$TOX_LOGGER" = "xno"; then
|
||||
AC_MSG_WARN([Logging disabled!])
|
||||
else
|
||||
if test "x$withval" = "xTRACE"; then
|
||||
|
@ -127,7 +127,7 @@ AC_ARG_WITH(log-path,
|
|||
AC_HELP_STRING([--with-log-path=DIR],
|
||||
[Path of logger output]),
|
||||
[
|
||||
if test "x$LOGGING" = "xno"; then
|
||||
if test "x$TOX_LOGGER" = "xno"; then
|
||||
AC_MSG_WARN([Logging disabled!])
|
||||
else
|
||||
AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger])
|
||||
|
|
615
other/apidsl/toxav.in.h
Normal file
615
other/apidsl/toxav.in.h
Normal file
|
@ -0,0 +1,615 @@
|
|||
%{
|
||||
/* toxav.h
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOXAV_H
|
||||
#define TOXAV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
%}
|
||||
|
||||
/** \page av Public audio/video API for Tox clients.
|
||||
*
|
||||
* This API can handle multiple calls. Each call has its state, in very rare
|
||||
* occasions the library can change the state of the call without apps knowledge.
|
||||
*
|
||||
*/
|
||||
|
||||
/** \subsection events Events and callbacks
|
||||
*
|
||||
* As in Core API, events are handled by callbacks. One callback can be
|
||||
* registered per event. All events have a callback function type named
|
||||
* `toxav_{event}_cb` and a function to register it named `tox_callback_{event}`.
|
||||
* Passing a NULL callback will result in no callback being registered for that
|
||||
* event. Only one callback per event can be registered, so if a client needs
|
||||
* multiple event listeners, it needs to implement the dispatch functionality
|
||||
* itself. Unlike Core API, lack of some event handlers will cause the the
|
||||
* library to drop calls before they are started. Hanging up call from a
|
||||
* callback causes undefined behaviour.
|
||||
*
|
||||
*/
|
||||
|
||||
/** \subsection threading Threading implications
|
||||
*
|
||||
* Unlike the Core API, this API is fully thread-safe. The library will ensure
|
||||
* the proper synchronization of parallel calls.
|
||||
*
|
||||
* A common way to run ToxAV (multiple or single instance) is to have a thread,
|
||||
* separate from tox instance thread, running a simple ${toxAV.iterate} loop,
|
||||
* sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration.
|
||||
*
|
||||
* An important thing to note is that events are triggered from both tox and
|
||||
* toxav thread (see above). audio and video receive frame events are triggered
|
||||
* from toxav thread while all the other events are triggered from tox thread.
|
||||
*
|
||||
* Tox thread has priority with mutex mechanisms. Any api function can
|
||||
* fail if mutexes are held by tox thread in which case they will set SYNC
|
||||
* error code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* External Tox type.
|
||||
*/
|
||||
class tox {
|
||||
struct this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ToxAV.
|
||||
*/
|
||||
class toxAV {
|
||||
|
||||
/**
|
||||
* The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
|
||||
* instance, and Tox instance can have only one ToxAV instance. One must make
|
||||
* sure to close ToxAV instance prior closing Tox instance otherwise undefined
|
||||
* behaviour occurs. Upon closing of ToxAV instance, all active calls will be
|
||||
* forcibly terminated without notifying peers.
|
||||
*
|
||||
*/
|
||||
struct this;
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: API version
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* The major version number. Incremented when the API or ABI changes in an
|
||||
* incompatible way.
|
||||
*/
|
||||
#define TOXAV_VERSION_MAJOR 0u
|
||||
/**
|
||||
* The minor version number. Incremented when functionality is added without
|
||||
* breaking the API or ABI. Set to 0 when the major version number is
|
||||
* incremented.
|
||||
*/
|
||||
#define TOXAV_VERSION_MINOR 0u
|
||||
/**
|
||||
* The patch or revision number. Incremented when bugfixes are applied without
|
||||
* changing any functionality or API or ABI.
|
||||
*/
|
||||
#define TOXAV_VERSION_PATCH 0u
|
||||
|
||||
/**
|
||||
* A macro to check at preprocessing time whether the client code is compatible
|
||||
* with the installed version of ToxAV.
|
||||
*/
|
||||
#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
|
||||
(TOXAV_VERSION_MAJOR == MAJOR && \
|
||||
(TOXAV_VERSION_MINOR > MINOR || \
|
||||
(TOXAV_VERSION_MINOR == MINOR && \
|
||||
TOXAV_VERSION_PATCH >= PATCH)))
|
||||
|
||||
/**
|
||||
* A macro to make compilation fail if the client code is not compatible with
|
||||
* the installed version of ToxAV.
|
||||
*/
|
||||
#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
|
||||
typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
|
||||
|
||||
/**
|
||||
* A convenience macro to call ${version.is_compatible} with the currently
|
||||
* compiling API version.
|
||||
*/
|
||||
#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
|
||||
toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
|
||||
|
||||
|
||||
static namespace version {
|
||||
|
||||
/**
|
||||
* Return the major version number of the library. Can be used to display the
|
||||
* ToxAV library version or to check whether the client is compatible with the
|
||||
* dynamically linked version of ToxAV.
|
||||
*/
|
||||
uint32_t major();
|
||||
|
||||
/**
|
||||
* Return the minor version number of the library.
|
||||
*/
|
||||
uint32_t minor();
|
||||
|
||||
/**
|
||||
* Return the patch number of the library.
|
||||
*/
|
||||
uint32_t patch();
|
||||
|
||||
/**
|
||||
* Return whether the compiled library version is compatible with the passed
|
||||
* version numbers.
|
||||
*/
|
||||
bool is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
|
||||
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Creation and destruction
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* Start new A/V session. There can only be only one session per Tox instance.
|
||||
*/
|
||||
static this new (tox::this *tox) {
|
||||
NULL,
|
||||
/**
|
||||
* Memory allocation failure while trying to allocate structures required for
|
||||
* the A/V session.
|
||||
*/
|
||||
MALLOC,
|
||||
/**
|
||||
* Attempted to create a second session for the same Tox instance.
|
||||
*/
|
||||
MULTIPLE,
|
||||
}
|
||||
/**
|
||||
* Releases all resources associated with the A/V session.
|
||||
*
|
||||
* If any calls were ongoing, these will be forcibly terminated without
|
||||
* notifying peers. After calling this function, no other functions may be
|
||||
* called and the av pointer becomes invalid.
|
||||
*/
|
||||
void kill();
|
||||
/**
|
||||
* Returns the Tox instance the A/V object was created for.
|
||||
*/
|
||||
tox::this *tox { get(); }
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V event loop
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* Returns the interval in milliseconds when the next toxav_iterate call should
|
||||
* be. If no call is active at the moment, this function returns 200.
|
||||
*/
|
||||
const uint32_t iteration_interval();
|
||||
/**
|
||||
* Main loop for the session. This function needs to be called in intervals of
|
||||
* toxav_iteration_interval() milliseconds. It is best called in the separate
|
||||
* thread from tox_iterate.
|
||||
*/
|
||||
void iterate();
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call setup
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* Call a friend. This will start ringing the friend.
|
||||
*
|
||||
* It is the client's responsibility to stop ringing after a certain timeout,
|
||||
* if such behaviour is desired. If the client does not stop ringing, the
|
||||
* library will not stop until the friend is disconnected. Audio and video
|
||||
* receiving are both enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that should be called.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
|
||||
/**
|
||||
* A resource allocation error occurred while trying to create the structures
|
||||
* required for the call.
|
||||
*/
|
||||
MALLOC,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
SYNC,
|
||||
/**
|
||||
* The friend number did not designate a valid friend.
|
||||
*/
|
||||
FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* The friend was valid, but not currently connected.
|
||||
*/
|
||||
FRIEND_NOT_CONNECTED,
|
||||
/**
|
||||
* Attempted to call a friend while already in an audio or video call with
|
||||
* them.
|
||||
*/
|
||||
FRIEND_ALREADY_IN_CALL,
|
||||
/**
|
||||
* Audio or video bit rate is invalid.
|
||||
*/
|
||||
INVALID_BIT_RATE,
|
||||
}
|
||||
event call {
|
||||
/**
|
||||
* The function type for the ${event call} callback.
|
||||
*
|
||||
* @param friend_number The friend number from which the call is incoming.
|
||||
* @param audio_enabled True if friend is sending audio.
|
||||
* @param video_enabled True if friend is sending video.
|
||||
*/
|
||||
typedef void(uint32_t friend_number, bool audio_enabled, bool video_enabled);
|
||||
}
|
||||
/**
|
||||
* Accept an incoming call.
|
||||
*
|
||||
* If answering fails for any reason, the call will still be pending and it is
|
||||
* possible to try and answer it later. Audio and video receiving are both
|
||||
* enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that is calling.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
SYNC,
|
||||
/**
|
||||
* Failed to initialize codecs for call session. Note that codec initiation
|
||||
* will fail if there is no receive callback registered for either audio or
|
||||
* video.
|
||||
*/
|
||||
CODEC_INITIALIZATION,
|
||||
/**
|
||||
* The friend number did not designate a valid friend.
|
||||
*/
|
||||
FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* The friend was valid, but they are not currently trying to initiate a call.
|
||||
* This is also returned if this client is already in a call with the friend.
|
||||
*/
|
||||
FRIEND_NOT_CALLING,
|
||||
/**
|
||||
* Audio or video bit rate is invalid.
|
||||
*/
|
||||
INVALID_BIT_RATE,
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call state graph
|
||||
*
|
||||
******************************************************************************/
|
||||
bitmask FRIEND_CALL_STATE {
|
||||
/**
|
||||
* Set by the AV core if an error occurred on the remote end or if friend
|
||||
* timed out. This is the final state after which no more state
|
||||
* transitions can occur for the call. This call state will never be triggered
|
||||
* in combination with other call states.
|
||||
*/
|
||||
ERROR,
|
||||
/**
|
||||
* The call has finished. This is the final state after which no more state
|
||||
* transitions can occur for the call. This call state will never be
|
||||
* triggered in combination with other call states.
|
||||
*/
|
||||
FINISHED,
|
||||
/**
|
||||
* The flag that marks that friend is sending audio.
|
||||
*/
|
||||
SENDING_A,
|
||||
/**
|
||||
* The flag that marks that friend is sending video.
|
||||
*/
|
||||
SENDING_V,
|
||||
/**
|
||||
* The flag that marks that friend is receiving audio.
|
||||
*/
|
||||
ACCEPTING_A,
|
||||
/**
|
||||
* The flag that marks that friend is receiving video.
|
||||
*/
|
||||
ACCEPTING_V,
|
||||
}
|
||||
event call_state {
|
||||
/**
|
||||
* The function type for the ${event call_state} callback.
|
||||
*
|
||||
* @param friend_number The friend number for which the call state changed.
|
||||
* @param state The bitmask of the new call state which is guaranteed to be
|
||||
* different than the previous state. The state is set to 0 when the call is
|
||||
* paused. The bitmask represents all the activities currently performed by the
|
||||
* friend.
|
||||
*/
|
||||
typedef void(uint32_t friend_number, uint32_t state);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call control
|
||||
*
|
||||
******************************************************************************/
|
||||
enum class CALL_CONTROL {
|
||||
/**
|
||||
* Resume a previously paused call. Only valid if the pause was caused by this
|
||||
* client, if not, this control is ignored. Not valid before the call is accepted.
|
||||
*/
|
||||
RESUME,
|
||||
/**
|
||||
* Put a call on hold. Not valid before the call is accepted.
|
||||
*/
|
||||
PAUSE,
|
||||
/**
|
||||
* Reject a call if it was not answered, yet. Cancel a call after it was
|
||||
* answered.
|
||||
*/
|
||||
CANCEL,
|
||||
/**
|
||||
* Request that the friend stops sending audio. Regardless of the friend's
|
||||
* compliance, this will cause the ${event audio.receive_frame} event to stop being
|
||||
* triggered on receiving an audio frame from the friend.
|
||||
*/
|
||||
MUTE_AUDIO,
|
||||
/**
|
||||
* Calling this control will notify client to start sending audio again.
|
||||
*/
|
||||
UNMUTE_AUDIO,
|
||||
/**
|
||||
* Request that the friend stops sending video. Regardless of the friend's
|
||||
* compliance, this will cause the ${event video.receive_frame} event to stop being
|
||||
* triggered on receiving a video frame from the friend.
|
||||
*/
|
||||
HIDE_VIDEO,
|
||||
/**
|
||||
* Calling this control will notify client to start sending video again.
|
||||
*/
|
||||
SHOW_VIDEO,
|
||||
}
|
||||
/**
|
||||
* Sends a call control command to a friend.
|
||||
*
|
||||
* @param friend_number The friend number of the friend this client is in a call
|
||||
* with.
|
||||
* @param control The control command to send.
|
||||
*
|
||||
* @return true on success.
|
||||
*/
|
||||
bool call_control (uint32_t friend_number, CALL_CONTROL control) {
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
SYNC,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend. Before the call is
|
||||
* answered, only CANCEL is a valid control.
|
||||
*/
|
||||
FRIEND_NOT_IN_CALL,
|
||||
/**
|
||||
* Happens if user tried to pause an already paused call or if trying to
|
||||
* resume a call that is not paused.
|
||||
*/
|
||||
INVALID_TRANSITION,
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Controlling bit rates
|
||||
*
|
||||
******************************************************************************/
|
||||
namespace bit_rate {
|
||||
/**
|
||||
* Set the audio bit rate to be used in subsequent audio/video frames.
|
||||
*
|
||||
* @param friend_number The friend number.
|
||||
* @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
|
||||
* audio sending. Set to -1 to leave unchanged.
|
||||
* @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
|
||||
* video sending. Set to -1 to leave unchanged.
|
||||
*
|
||||
*/
|
||||
bool set(uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate) {
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
SYNC,
|
||||
/**
|
||||
* The bit rate passed was not one of the supported values.
|
||||
*/
|
||||
INVALID,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend.
|
||||
*/
|
||||
FRIEND_NOT_IN_CALL,
|
||||
}
|
||||
event status {
|
||||
/**
|
||||
* The function type for the ${event status} callback. The event is triggered
|
||||
* when the network becomes too saturated for current bit rates at which
|
||||
* point core suggests new bit rates.
|
||||
*
|
||||
* @param friend_number The friend number.
|
||||
* @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
|
||||
* @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
|
||||
*/
|
||||
typedef void(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate);
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V sending
|
||||
*
|
||||
******************************************************************************/
|
||||
error for send_frame {
|
||||
/**
|
||||
* In case of video, one of Y, U, or V was NULL. In case of audio, the samples
|
||||
* data pointer was NULL.
|
||||
*/
|
||||
NULL,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend.
|
||||
*/
|
||||
FRIEND_NOT_IN_CALL,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
SYNC,
|
||||
/**
|
||||
* One of the frame parameters was invalid. E.g. the resolution may be too
|
||||
* small or too large, or the audio sampling rate may be unsupported.
|
||||
*/
|
||||
INVALID,
|
||||
/**
|
||||
* Either friend turned off audio or video receiving or we turned off sending
|
||||
* for the said payload.
|
||||
*/
|
||||
PAYLOAD_TYPE_DISABLED,
|
||||
/**
|
||||
* Failed to push frame through rtp interface.
|
||||
*/
|
||||
RTP_FAILED,
|
||||
}
|
||||
namespace audio {
|
||||
/**
|
||||
* Send an audio frame to a friend.
|
||||
*
|
||||
* The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
|
||||
* Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
|
||||
* For mono audio, this has no meaning, every sample is subsequent. For stereo,
|
||||
* this means the expected format is LRLRLR... with samples for left and right
|
||||
* alternating.
|
||||
*
|
||||
* @param friend_number The friend number of the friend to which to send an
|
||||
* audio frame.
|
||||
* @param pcm An array of audio samples. The size of this array must be
|
||||
* sample_count * channels.
|
||||
* @param sample_count Number of samples in this frame. Valid numbers here are
|
||||
* ((sample rate) * (audio length) / 1000), where audio length can be
|
||||
* 2.5, 5, 10, 20, 40 or 60 millseconds.
|
||||
* @param channels Number of audio channels. Supported values are 1 and 2.
|
||||
* @param sampling_rate Audio sampling rate used in this frame. Valid sampling
|
||||
* rates are 8000, 12000, 16000, 24000, or 48000.
|
||||
*/
|
||||
bool send_frame(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
|
||||
uint8_t channels, uint32_t sampling_rate) with error for send_frame;
|
||||
}
|
||||
namespace video {
|
||||
/**
|
||||
* Send a video frame to a friend.
|
||||
*
|
||||
* Y - plane should be of size: height * width
|
||||
* U - plane should be of size: (height/2) * (width/2)
|
||||
* V - plane should be of size: (height/2) * (width/2)
|
||||
*
|
||||
* @param friend_number The friend number of the friend to which to send a video
|
||||
* frame.
|
||||
* @param width Width of the frame in pixels.
|
||||
* @param height Height of the frame in pixels.
|
||||
* @param y Y (Luminance) plane data.
|
||||
* @param u U (Chroma) plane data.
|
||||
* @param v V (Chroma) plane data.
|
||||
*/
|
||||
bool send_frame(uint32_t friend_number, uint16_t width, uint16_t height,
|
||||
const uint8_t *y, const uint8_t *u, const uint8_t *v) with error for send_frame;
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V receiving
|
||||
*
|
||||
******************************************************************************/
|
||||
namespace audio {
|
||||
event receive_frame {
|
||||
/**
|
||||
* The function type for the ${event receive_frame} callback. The callback can be
|
||||
* called multiple times per single iteration depending on the amount of queued
|
||||
* frames in the buffer. The received format is the same as in send function.
|
||||
*
|
||||
* @param friend_number The friend number of the friend who sent an audio frame.
|
||||
* @param pcm An array of audio samples (sample_count * channels elements).
|
||||
* @param sample_count The number of audio samples per channel in the PCM array.
|
||||
* @param channels Number of audio channels.
|
||||
* @param sampling_rate Sampling rate used in this frame.
|
||||
*
|
||||
*/
|
||||
typedef void(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
|
||||
uint8_t channels, uint32_t sampling_rate);
|
||||
}
|
||||
}
|
||||
namespace video {
|
||||
event receive_frame {
|
||||
/**
|
||||
* The function type for the ${event receive_frame} callback.
|
||||
*
|
||||
* @param friend_number The friend number of the friend who sent a video frame.
|
||||
* @param width Width of the frame in pixels.
|
||||
* @param height Height of the frame in pixels.
|
||||
* @param y
|
||||
* @param u
|
||||
* @param v Plane data.
|
||||
* The size of plane data is derived from width and height where
|
||||
* Y = MAX(width, abs(ystride)) * height,
|
||||
* U = MAX(width/2, abs(ustride)) * (height/2) and
|
||||
* V = MAX(width/2, abs(vstride)) * (height/2).
|
||||
* @param ystride
|
||||
* @param ustride
|
||||
* @param vstride Strides data. Strides represent padding for each plane
|
||||
* that may or may not be present. You must handle strides in
|
||||
* your image processing code. Strides are negative if the
|
||||
* image is bottom-up hence why you MUST abs() it when
|
||||
* calculating plane buffer size.
|
||||
*/
|
||||
typedef void(uint32_t friend_number, uint16_t width, uint16_t height,
|
||||
const uint8_t *y, const uint8_t *u, const uint8_t *v,
|
||||
int32_t ystride, int32_t ustride, int32_t vstride);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
%{
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* TOXAV_H */
|
||||
%}
|
766
testing/av_test.c
Normal file
766
testing/av_test.c
Normal file
|
@ -0,0 +1,766 @@
|
|||
/** av_test.c
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Compile with (Linux only; in newly created directory toxcore/dir_name):
|
||||
* gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \
|
||||
* -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio
|
||||
*/
|
||||
|
||||
|
||||
#include "../toxav/toxav.h"
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/network.h" /* current_time_monotonic() */
|
||||
|
||||
/* Playing audio data */
|
||||
#include <portaudio.h>
|
||||
/* Reading audio */
|
||||
#include <sndfile.h>
|
||||
|
||||
/* Reading and Displaying video data */
|
||||
#include <opencv/cv.h>
|
||||
#include <opencv/highgui.h>
|
||||
#include <opencv/cvwimage.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define c_sleep(x) usleep(1000*x)
|
||||
|
||||
|
||||
#define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X)
|
||||
|
||||
// RGB -> YUV
|
||||
#define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
|
||||
#define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
|
||||
#define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
|
||||
|
||||
// YUV -> RGB
|
||||
#define C(Y) ((Y) - 16 )
|
||||
#define D(U) ((U) - 128 )
|
||||
#define E(V) ((V) - 128 )
|
||||
|
||||
#define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8)
|
||||
#define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
|
||||
#define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8)
|
||||
|
||||
|
||||
#define TEST_TRANSFER_A 0
|
||||
#define TEST_TRANSFER_V 1
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool incoming;
|
||||
uint32_t state;
|
||||
pthread_mutex_t arb_mutex[1];
|
||||
RingBuffer *arb; /* Audio ring buffer */
|
||||
|
||||
} CallControl;
|
||||
|
||||
struct toxav_thread_data {
|
||||
ToxAV *AliceAV;
|
||||
ToxAV *BobAV;
|
||||
int32_t sig;
|
||||
};
|
||||
|
||||
const char *vdout = "AV Test"; /* Video output */
|
||||
PaStream *adout = NULL; /* Audio output */
|
||||
|
||||
typedef struct {
|
||||
uint16_t size;
|
||||
int16_t data[];
|
||||
} frame;
|
||||
|
||||
void *pa_write_thread (void *d)
|
||||
{
|
||||
/* The purpose of this thread is to make sure Pa_WriteStream will not block
|
||||
* toxav_iterate thread
|
||||
*/
|
||||
CallControl *cc = d;
|
||||
|
||||
while (Pa_IsStreamActive(adout)) {
|
||||
frame *f;
|
||||
pthread_mutex_lock(cc->arb_mutex);
|
||||
|
||||
if (rb_read(cc->arb, (void **)&f)) {
|
||||
pthread_mutex_unlock(cc->arb_mutex);
|
||||
Pa_WriteStream(adout, f->data, f->size);
|
||||
free(f);
|
||||
} else {
|
||||
pthread_mutex_unlock(cc->arb_mutex);
|
||||
c_sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks
|
||||
*/
|
||||
void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
|
||||
{
|
||||
printf("Handling CALL callback\n");
|
||||
((CallControl *)user_data)->incoming = true;
|
||||
}
|
||||
void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
|
||||
{
|
||||
printf("Handling CALL STATE callback: %d\n", state);
|
||||
((CallControl *)user_data)->state = state;
|
||||
}
|
||||
void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
uint16_t width, uint16_t height,
|
||||
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||
void *user_data)
|
||||
{
|
||||
ystride = abs(ystride);
|
||||
ustride = abs(ustride);
|
||||
vstride = abs(vstride);
|
||||
|
||||
uint16_t *img_data = malloc(height * width * 6);
|
||||
|
||||
unsigned long int i, j;
|
||||
|
||||
for (i = 0; i < height; ++i) {
|
||||
for (j = 0; j < width; ++j) {
|
||||
uint8_t *point = (uint8_t *) img_data + 3 * ((i * width) + j);
|
||||
int yx = y[(i * ystride) + j];
|
||||
int ux = u[((i / 2) * ustride) + (j / 2)];
|
||||
int vx = v[((i / 2) * vstride) + (j / 2)];
|
||||
|
||||
point[0] = YUV2R(yx, ux, vx);
|
||||
point[1] = YUV2G(yx, ux, vx);
|
||||
point[2] = YUV2B(yx, ux, vx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CvMat mat = cvMat(height, width, CV_8UC3, img_data);
|
||||
|
||||
CvSize sz = {.height = height, .width = width};
|
||||
|
||||
IplImage *header = cvCreateImageHeader(sz, 1, 3);
|
||||
IplImage *img = cvGetImage(&mat, header);
|
||||
cvShowImage(vdout, img);
|
||||
free(img_data);
|
||||
}
|
||||
void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
int16_t const *pcm,
|
||||
size_t sample_count,
|
||||
uint8_t channels,
|
||||
uint32_t sampling_rate,
|
||||
void *user_data)
|
||||
{
|
||||
CallControl *cc = user_data;
|
||||
frame *f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels);
|
||||
memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels);
|
||||
f->size = sample_count;
|
||||
|
||||
pthread_mutex_lock(cc->arb_mutex);
|
||||
free(rb_write(cc->arb, f));
|
||||
pthread_mutex_unlock(cc->arb_mutex);
|
||||
}
|
||||
void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
|
||||
uint32_t audio_bit_rate, uint32_t video_bit_rate,
|
||||
void *user_data)
|
||||
{
|
||||
printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate);
|
||||
}
|
||||
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
|
||||
assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
void initialize_tox(Tox **bootstrap, ToxAV **AliceAV, CallControl *AliceCC, ToxAV **BobAV, CallControl *BobCC)
|
||||
{
|
||||
Tox *Alice;
|
||||
Tox *Bob;
|
||||
|
||||
struct Tox_Options opts;
|
||||
tox_options_default(&opts);
|
||||
|
||||
opts.end_port = 0;
|
||||
opts.ipv6_enabled = false;
|
||||
|
||||
{
|
||||
TOX_ERR_NEW error;
|
||||
|
||||
opts.start_port = 33445;
|
||||
*bootstrap = tox_new(&opts, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
opts.start_port = 33455;
|
||||
Alice = tox_new(&opts, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
opts.start_port = 33465;
|
||||
Bob = tox_new(&opts, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
}
|
||||
|
||||
printf("Created 3 instances of Tox\n");
|
||||
printf("Preparing network...\n");
|
||||
long long unsigned int cur_time = time(NULL);
|
||||
|
||||
uint32_t to_compare = 974536;
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
|
||||
tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
|
||||
tox_self_get_address(Alice, address);
|
||||
|
||||
|
||||
assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_iterate(*bootstrap);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
if (tox_self_get_connection_status(*bootstrap) &&
|
||||
tox_self_get_connection_status(Alice) &&
|
||||
tox_self_get_connection_status(Bob) && off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
|
||||
break;
|
||||
|
||||
c_sleep(20);
|
||||
}
|
||||
|
||||
|
||||
TOXAV_ERR_NEW rc;
|
||||
*AliceAV = toxav_new(Alice, &rc);
|
||||
assert(rc == TOXAV_ERR_NEW_OK);
|
||||
|
||||
*BobAV = toxav_new(Bob, &rc);
|
||||
assert(rc == TOXAV_ERR_NEW_OK);
|
||||
|
||||
|
||||
/* Alice */
|
||||
toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
|
||||
toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
|
||||
toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC);
|
||||
toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
|
||||
toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
|
||||
|
||||
/* Bob */
|
||||
toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
|
||||
toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
|
||||
toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC);
|
||||
toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
|
||||
toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
|
||||
|
||||
|
||||
printf("Created 2 instances of ToxAV\n");
|
||||
printf("All set after %llu seconds!\n", time(NULL) - cur_time);
|
||||
}
|
||||
int iterate_tox(Tox *bootstrap, ToxAV *AliceAV, ToxAV *BobAV)
|
||||
{
|
||||
tox_iterate(bootstrap);
|
||||
tox_iterate(toxav_get_tox(AliceAV));
|
||||
tox_iterate(toxav_get_tox(BobAV));
|
||||
|
||||
return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
|
||||
}
|
||||
void *iterate_toxav (void *data)
|
||||
{
|
||||
struct toxav_thread_data *data_cast = data;
|
||||
#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
|
||||
cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE);
|
||||
#endif
|
||||
|
||||
while (data_cast->sig == 0) {
|
||||
toxav_iterate(data_cast->AliceAV);
|
||||
toxav_iterate(data_cast->BobAV);
|
||||
int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV));
|
||||
|
||||
printf("\rIteration interval: %d ", rc);
|
||||
fflush(stdout);
|
||||
|
||||
#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
|
||||
|
||||
if (!rc)
|
||||
rc = 1;
|
||||
|
||||
cvWaitKey(rc);
|
||||
#else
|
||||
c_sleep(rc);
|
||||
#endif
|
||||
}
|
||||
|
||||
data_cast->sig = 1;
|
||||
|
||||
#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
|
||||
cvDestroyWindow(vdout);
|
||||
#endif
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int send_opencv_img(ToxAV *av, uint32_t friend_number, const IplImage *img)
|
||||
{
|
||||
int32_t strides[3] = { 1280, 640, 640 };
|
||||
uint8_t *planes[3] = {
|
||||
malloc(img->height * img->width),
|
||||
malloc(img->height * img->width / 4),
|
||||
malloc(img->height * img->width / 4),
|
||||
};
|
||||
|
||||
int x_chroma_shift = 1;
|
||||
int y_chroma_shift = 1;
|
||||
|
||||
int x, y;
|
||||
|
||||
for (y = 0; y < img->height; ++y) {
|
||||
for (x = 0; x < img->width; ++x) {
|
||||
uint8_t r = img->imageData[(x + y * img->width) * 3 + 0];
|
||||
uint8_t g = img->imageData[(x + y * img->width) * 3 + 1];
|
||||
uint8_t b = img->imageData[(x + y * img->width) * 3 + 2];
|
||||
|
||||
planes[0][x + y * strides[0]] = RGB2Y(r, g, b);
|
||||
|
||||
if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) {
|
||||
const int i = x / (1 << x_chroma_shift);
|
||||
const int j = y / (1 << y_chroma_shift);
|
||||
planes[1][i + j * strides[1]] = RGB2U(r, g, b);
|
||||
planes[2][i + j * strides[2]] = RGB2V(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rc = toxav_video_send_frame(av, friend_number, img->width, img->height,
|
||||
planes[0], planes[1], planes[2], NULL);
|
||||
free(planes[0]);
|
||||
free(planes[1]);
|
||||
free(planes[2]);
|
||||
return rc;
|
||||
}
|
||||
int print_audio_devices()
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < Pa_GetDeviceCount(); ++i) {
|
||||
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
||||
|
||||
if (info)
|
||||
printf("%d) %s\n", i, info->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int print_help (const char *name)
|
||||
{
|
||||
printf("Usage: %s -[a:v:o:dh]\n"
|
||||
"-a <path> audio input file\n"
|
||||
"-b <ms> audio frame duration\n"
|
||||
"-v <path> video input file\n"
|
||||
"-x <ms> video frame duration\n"
|
||||
"-o <idx> output audio device index\n"
|
||||
"-d print output audio devices\n"
|
||||
"-h print this help\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
freopen("/dev/zero", "w", stderr);
|
||||
Pa_Initialize();
|
||||
|
||||
struct stat st;
|
||||
|
||||
/* AV files for testing */
|
||||
const char *af_name = NULL;
|
||||
const char *vf_name = NULL;
|
||||
long audio_out_dev_idx = -1;
|
||||
|
||||
int32_t audio_frame_duration = 20;
|
||||
int32_t video_frame_duration = 10;
|
||||
|
||||
/* Parse settings */
|
||||
CHECK_ARG:
|
||||
|
||||
switch (getopt(argc, argv, "a:b:v:x:o:dh")) {
|
||||
case 'a':
|
||||
af_name = optarg;
|
||||
goto CHECK_ARG;
|
||||
|
||||
case 'b': {
|
||||
char *d;
|
||||
audio_frame_duration = strtol(optarg, &d, 10);
|
||||
|
||||
if (*d) {
|
||||
printf("Invalid value for argument: 'b'");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
goto CHECK_ARG;
|
||||
}
|
||||
|
||||
case 'v':
|
||||
vf_name = optarg;
|
||||
goto CHECK_ARG;
|
||||
|
||||
case 'x': {
|
||||
char *d;
|
||||
video_frame_duration = strtol(optarg, &d, 10);
|
||||
|
||||
if (*d) {
|
||||
printf("Invalid value for argument: 'x'");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
goto CHECK_ARG;
|
||||
}
|
||||
|
||||
case 'o': {
|
||||
char *d;
|
||||
audio_out_dev_idx = strtol(optarg, &d, 10);
|
||||
|
||||
if (*d) {
|
||||
printf("Invalid value for argument: 'o'");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
goto CHECK_ARG;
|
||||
}
|
||||
|
||||
case 'd':
|
||||
return print_audio_devices();
|
||||
|
||||
case 'h':
|
||||
return print_help(argv[0]);
|
||||
|
||||
case '?':
|
||||
exit(1);
|
||||
|
||||
case -1:
|
||||
;
|
||||
}
|
||||
|
||||
{ /* Check files */
|
||||
if (!af_name) {
|
||||
printf("Required audio input file!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!vf_name) {
|
||||
printf("Required video input file!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Check for files */
|
||||
if (stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) {
|
||||
printf("%s doesn't seem to be a regular file!\n", af_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) {
|
||||
printf("%s doesn't seem to be a regular file!\n", vf_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_out_dev_idx < 0)
|
||||
audio_out_dev_idx = Pa_GetDefaultOutputDevice();
|
||||
|
||||
const PaDeviceInfo *audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx);
|
||||
|
||||
if (!audio_dev) {
|
||||
fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Using audio device: %s\n", audio_dev->name);
|
||||
printf("Using audio file: %s\n", af_name);
|
||||
printf("Using video file: %s\n", vf_name);
|
||||
|
||||
/* START TOX NETWORK */
|
||||
|
||||
Tox *bootstrap;
|
||||
ToxAV *AliceAV;
|
||||
ToxAV *BobAV;
|
||||
|
||||
CallControl AliceCC;
|
||||
CallControl BobCC;
|
||||
|
||||
initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC);
|
||||
|
||||
if (TEST_TRANSFER_A) {
|
||||
SNDFILE *af_handle;
|
||||
SF_INFO af_info;
|
||||
|
||||
printf("\nTrying audio enc/dec...\n");
|
||||
|
||||
memset(&AliceCC, 0, sizeof(CallControl));
|
||||
memset(&BobCC, 0, sizeof(CallControl));
|
||||
|
||||
pthread_mutex_init(AliceCC.arb_mutex, NULL);
|
||||
pthread_mutex_init(BobCC.arb_mutex, NULL);
|
||||
|
||||
AliceCC.arb = rb_new(16);
|
||||
BobCC.arb = rb_new(16);
|
||||
|
||||
{ /* Call */
|
||||
TOXAV_ERR_CALL rc;
|
||||
toxav_call(AliceAV, 0, 48, 0, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_OK) {
|
||||
printf("toxav_call failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (!BobCC.incoming)
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
|
||||
{ /* Answer */
|
||||
TOXAV_ERR_ANSWER rc;
|
||||
toxav_answer(BobAV, 0, 48, 0, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_ANSWER_OK) {
|
||||
printf("toxav_answer failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (AliceCC.state == 0)
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
|
||||
/* Open audio file */
|
||||
af_handle = sf_open(af_name, SFM_READ, &af_info);
|
||||
|
||||
if (af_handle == NULL) {
|
||||
printf("Failed to open the file.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int16_t PCM[5760];
|
||||
|
||||
time_t start_time = time(NULL);
|
||||
time_t expected_time = af_info.frames / af_info.samplerate + 2;
|
||||
|
||||
|
||||
/* Start decode thread */
|
||||
struct toxav_thread_data data = {
|
||||
.AliceAV = AliceAV,
|
||||
.BobAV = BobAV,
|
||||
.sig = 0
|
||||
};
|
||||
|
||||
pthread_t dect;
|
||||
pthread_create(&dect, NULL, iterate_toxav, &data);
|
||||
pthread_detach(dect);
|
||||
|
||||
int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels;
|
||||
|
||||
struct PaStreamParameters output;
|
||||
output.device = audio_out_dev_idx;
|
||||
output.channelCount = af_info.channels;
|
||||
output.sampleFormat = paInt16;
|
||||
output.suggestedLatency = audio_dev->defaultHighOutputLatency;
|
||||
output.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL);
|
||||
assert(err == paNoError);
|
||||
|
||||
err = Pa_StartStream(adout);
|
||||
assert(err == paNoError);
|
||||
|
||||
// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
|
||||
|
||||
/* Start write thread */
|
||||
pthread_t t;
|
||||
pthread_create(&t, NULL, pa_write_thread, &BobCC);
|
||||
pthread_detach(t);
|
||||
|
||||
printf("Sample rate %d\n", af_info.samplerate);
|
||||
|
||||
while (start_time + expected_time > time(NULL) ) {
|
||||
uint64_t enc_start_time = current_time_monotonic();
|
||||
int64_t count = sf_read_short(af_handle, PCM, frame_size);
|
||||
|
||||
if (count > 0) {
|
||||
TOXAV_ERR_SEND_FRAME rc;
|
||||
|
||||
if (toxav_audio_send_frame(AliceAV, 0, PCM, count / af_info.channels, af_info.channels, af_info.samplerate,
|
||||
&rc) == false) {
|
||||
printf("Error sending frame of size %ld: %d\n", count, rc);
|
||||
}
|
||||
}
|
||||
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1));
|
||||
}
|
||||
|
||||
printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time);
|
||||
|
||||
Pa_StopStream(adout);
|
||||
sf_close(af_handle);
|
||||
|
||||
{ /* Hangup */
|
||||
TOXAV_ERR_CALL_CONTROL rc;
|
||||
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
|
||||
printf("toxav_call_control failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
|
||||
|
||||
/* Stop decode thread */
|
||||
data.sig = -1;
|
||||
|
||||
while (data.sig != 1)
|
||||
pthread_yield();
|
||||
|
||||
pthread_mutex_destroy(AliceCC.arb_mutex);
|
||||
pthread_mutex_destroy(BobCC.arb_mutex);
|
||||
|
||||
void *f = NULL;
|
||||
|
||||
while (rb_read(AliceCC.arb, &f))
|
||||
free(f);
|
||||
|
||||
while (rb_read(BobCC.arb, &f))
|
||||
free(f);
|
||||
|
||||
printf("Success!");
|
||||
}
|
||||
|
||||
if (TEST_TRANSFER_V) {
|
||||
printf("\nTrying video enc/dec...\n");
|
||||
|
||||
memset(&AliceCC, 0, sizeof(CallControl));
|
||||
memset(&BobCC, 0, sizeof(CallControl));
|
||||
|
||||
{ /* Call */
|
||||
TOXAV_ERR_CALL rc;
|
||||
toxav_call(AliceAV, 0, 0, 2000, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_OK) {
|
||||
printf("toxav_call failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (!BobCC.incoming)
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
|
||||
{ /* Answer */
|
||||
TOXAV_ERR_ANSWER rc;
|
||||
toxav_answer(BobAV, 0, 0, 5000, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_ANSWER_OK) {
|
||||
printf("toxav_answer failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
|
||||
/* Start decode thread */
|
||||
struct toxav_thread_data data = {
|
||||
.AliceAV = AliceAV,
|
||||
.BobAV = BobAV,
|
||||
.sig = 0
|
||||
};
|
||||
|
||||
pthread_t dect;
|
||||
pthread_create(&dect, NULL, iterate_toxav, &data);
|
||||
pthread_detach(dect);
|
||||
|
||||
CvCapture *capture = cvCreateFileCapture(vf_name);
|
||||
|
||||
if (!capture) {
|
||||
printf("Failed to open video file: %s\n", vf_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL);
|
||||
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
while (start_time + 90 > time(NULL)) {
|
||||
IplImage *frame = cvQueryFrame(capture );
|
||||
|
||||
if (!frame)
|
||||
break;
|
||||
|
||||
send_opencv_img(AliceAV, 0, frame);
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
c_sleep(10);
|
||||
}
|
||||
|
||||
cvReleaseCapture(&capture);
|
||||
|
||||
{ /* Hangup */
|
||||
TOXAV_ERR_CALL_CONTROL rc;
|
||||
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
|
||||
|
||||
if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
|
||||
printf("toxav_call_control failed: %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
iterate_tox(bootstrap, AliceAV, BobAV);
|
||||
assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
|
||||
|
||||
/* Stop decode thread */
|
||||
printf("Stopping decode thread\n");
|
||||
data.sig = -1;
|
||||
|
||||
while (data.sig != 1)
|
||||
pthread_yield();
|
||||
|
||||
printf("Success!");
|
||||
}
|
||||
|
||||
|
||||
Tox *Alice = toxav_get_tox(AliceAV);
|
||||
Tox *Bob = toxav_get_tox(BobAV);
|
||||
toxav_kill(BobAV);
|
||||
toxav_kill(AliceAV);
|
||||
tox_kill(Bob);
|
||||
tox_kill(Alice);
|
||||
tox_kill(bootstrap);
|
||||
|
||||
printf("\nTest successful!\n");
|
||||
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
}
|
|
@ -1,38 +1,42 @@
|
|||
if BUILD_AV
|
||||
|
||||
lib_LTLIBRARIES += libtoxav.la
|
||||
libtoxav_la_include_HEADERS = ../toxav/toxav.h
|
||||
libtoxav_la_includedir = $(includedir)/tox
|
||||
lib_LTLIBRARIES += libtoxav.la
|
||||
libtoxav_la_include_HEADERS = ../toxav/toxav.h
|
||||
libtoxav_la_includedir = $(includedir)/tox
|
||||
|
||||
libtoxav_la_SOURCES = ../toxav/rtp.h \
|
||||
../toxav/rtp.c \
|
||||
../toxav/msi.h \
|
||||
../toxav/msi.c \
|
||||
../toxav/group.h \
|
||||
../toxav/group.c \
|
||||
../toxav/codec.h \
|
||||
../toxav/codec.c \
|
||||
../toxav/toxav.h \
|
||||
../toxav/toxav.c
|
||||
|
||||
../toxav/rtp.c \
|
||||
../toxav/msi.h \
|
||||
../toxav/msi.c \
|
||||
../toxav/group.h \
|
||||
../toxav/group.c \
|
||||
../toxav/audio.h \
|
||||
../toxav/audio.c \
|
||||
../toxav/video.h \
|
||||
../toxav/video.c \
|
||||
../toxav/bwcontroler.h \
|
||||
../toxav/bwcontroler.c \
|
||||
../toxav/toxav.h \
|
||||
../toxav/toxav.c \
|
||||
../toxav/toxav_old.c
|
||||
|
||||
libtoxav_la_CFLAGS = -I../toxcore \
|
||||
-I../toxav \
|
||||
$(LIBSODIUM_CFLAGS) \
|
||||
$(NACL_CFLAGS) \
|
||||
$(AV_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS)
|
||||
-I../toxav \
|
||||
$(LIBSODIUM_CFLAGS) \
|
||||
$(NACL_CFLAGS) \
|
||||
$(AV_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS)
|
||||
|
||||
libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
|
||||
$(LIBSODIUM_LDFLAGS) \
|
||||
$(NACL_LDFLAGS) \
|
||||
$(EXTRA_LT_LDFLAGS) \
|
||||
$(WINSOCK2_LIBS)
|
||||
$(LIBSODIUM_LDFLAGS) \
|
||||
$(NACL_LDFLAGS) \
|
||||
$(EXTRA_LT_LDFLAGS) \
|
||||
$(WINSOCK2_LIBS)
|
||||
|
||||
libtoxav_la_LIBADD = libtoxcore.la \
|
||||
$(LIBSODIUM_LIBS) \
|
||||
$(NACL_LIBS) \
|
||||
$(PTHREAD_LIBS) \
|
||||
$(AV_LIBS)
|
||||
$(LIBSODIUM_LIBS) \
|
||||
$(NACL_LIBS) \
|
||||
$(PTHREAD_LIBS) \
|
||||
$(AV_LIBS)
|
||||
|
||||
endif
|
439
toxav/audio.c
Normal file
439
toxav/audio.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
/** audio.c
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#include "../toxcore/logger.h"
|
||||
|
||||
static struct JitterBuffer *jbuf_new(uint32_t capacity);
|
||||
static void jbuf_clear(struct JitterBuffer *q);
|
||||
static void jbuf_free(struct JitterBuffer *q);
|
||||
static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m);
|
||||
static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
|
||||
OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
|
||||
bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
|
||||
int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
|
||||
bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels);
|
||||
|
||||
|
||||
|
||||
ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
|
||||
{
|
||||
ACSession *ac = calloc(sizeof(ACSession), 1);
|
||||
|
||||
if (!ac) {
|
||||
LOGGER_WARNING("Allocation failed! Application might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(ac->queue_mutex) != 0) {
|
||||
LOGGER_WARNING("Failed to create recursive mutex!");
|
||||
free(ac);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int status;
|
||||
ac->decoder = opus_decoder_create(48000, 2, &status);
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
|
||||
goto BASE_CLEANUP;
|
||||
}
|
||||
|
||||
if (!(ac->j_buf = jbuf_new(3))) {
|
||||
LOGGER_WARNING("Jitter buffer creaton failed!");
|
||||
opus_decoder_destroy(ac->decoder);
|
||||
goto BASE_CLEANUP;
|
||||
}
|
||||
|
||||
/* Initialize encoders with default values */
|
||||
ac->encoder = create_audio_encoder(48000, 48000, 2);
|
||||
|
||||
if (ac->encoder == NULL)
|
||||
goto DECODER_CLEANUP;
|
||||
|
||||
ac->le_bit_rate = 48000;
|
||||
ac->le_sample_rate = 48000;
|
||||
ac->le_channel_count = 2;
|
||||
|
||||
ac->ld_channel_count = 2;
|
||||
ac->ld_sample_rate = 48000;
|
||||
ac->ldrts = 0; /* Make it possible to reconfigure straight away */
|
||||
|
||||
/* These need to be set in order to properly
|
||||
* do error correction with opus */
|
||||
ac->lp_frame_duration = 120;
|
||||
ac->lp_sampling_rate = 48000;
|
||||
ac->lp_channel_count = 1;
|
||||
|
||||
ac->av = av;
|
||||
ac->friend_number = friend_number;
|
||||
ac->acb.first = cb;
|
||||
ac->acb.second = cb_data;
|
||||
|
||||
return ac;
|
||||
|
||||
DECODER_CLEANUP:
|
||||
opus_decoder_destroy(ac->decoder);
|
||||
jbuf_free(ac->j_buf);
|
||||
BASE_CLEANUP:
|
||||
pthread_mutex_destroy(ac->queue_mutex);
|
||||
free(ac);
|
||||
return NULL;
|
||||
}
|
||||
void ac_kill(ACSession *ac)
|
||||
{
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
opus_encoder_destroy(ac->encoder);
|
||||
opus_decoder_destroy(ac->decoder);
|
||||
jbuf_free(ac->j_buf);
|
||||
|
||||
pthread_mutex_destroy(ac->queue_mutex);
|
||||
|
||||
LOGGER_DEBUG("Terminated audio handler: %p", ac);
|
||||
free(ac);
|
||||
}
|
||||
void ac_iterate(ACSession *ac)
|
||||
{
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
/* TODO fix this and jitter buffering */
|
||||
|
||||
/* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */
|
||||
int16_t tmp[5760 * 2];
|
||||
|
||||
struct RTPMessage *msg;
|
||||
int rc = 0;
|
||||
|
||||
pthread_mutex_lock(ac->queue_mutex);
|
||||
|
||||
while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
|
||||
pthread_mutex_unlock(ac->queue_mutex);
|
||||
|
||||
if (rc == 2) {
|
||||
LOGGER_DEBUG("OPUS correction");
|
||||
int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000;
|
||||
rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
|
||||
} else {
|
||||
/* Get values from packet and decode. */
|
||||
/* NOTE: This didn't work very well
|
||||
rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data));
|
||||
if (rc != -1) {
|
||||
cs->last_packet_sampling_rate = rc;
|
||||
} else {
|
||||
LOGGER_WARNING("Failed to load packet values!");
|
||||
rtp_free_msg(msg);
|
||||
continue;
|
||||
}*/
|
||||
|
||||
|
||||
/* Pick up sampling rate from packet */
|
||||
memcpy(&ac->lp_sampling_rate, msg->data, 4);
|
||||
ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate);
|
||||
|
||||
ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
|
||||
|
||||
/** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
|
||||
* it didn't work quite well.
|
||||
*/
|
||||
if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
|
||||
LOGGER_WARNING("Failed to reconfigure decoder!");
|
||||
free(msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
|
||||
} else if (ac->acb.first) {
|
||||
ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate;
|
||||
|
||||
ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count,
|
||||
ac->lp_sampling_rate, ac->acb.second);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(ac->queue_mutex);
|
||||
}
|
||||
int ac_queue_message(void *acp, struct RTPMessage *msg)
|
||||
{
|
||||
if (!acp || !msg)
|
||||
return -1;
|
||||
|
||||
if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
|
||||
LOGGER_WARNING("Got dummy!");
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) {
|
||||
LOGGER_WARNING("Invalid payload type!");
|
||||
free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ACSession *ac = acp;
|
||||
|
||||
pthread_mutex_lock(ac->queue_mutex);
|
||||
int rc = jbuf_write(ac->j_buf, msg);
|
||||
pthread_mutex_unlock(ac->queue_mutex);
|
||||
|
||||
if (rc == -1) {
|
||||
LOGGER_WARNING("Could not queue the message!");
|
||||
free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
|
||||
{
|
||||
if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate,
|
||||
sampling_rate, channels,
|
||||
&ac->le_bit_rate,
|
||||
&ac->le_sample_rate,
|
||||
&ac->le_channel_count))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct JitterBuffer {
|
||||
struct RTPMessage **queue;
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
uint16_t bottom;
|
||||
uint16_t top;
|
||||
};
|
||||
|
||||
static struct JitterBuffer *jbuf_new(uint32_t capacity)
|
||||
{
|
||||
unsigned int size = 1;
|
||||
|
||||
while (size <= (capacity * 4)) {
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
struct JitterBuffer *q;
|
||||
|
||||
if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL;
|
||||
|
||||
if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
q->size = size;
|
||||
q->capacity = capacity;
|
||||
return q;
|
||||
}
|
||||
static void jbuf_clear(struct JitterBuffer *q)
|
||||
{
|
||||
for (; q->bottom != q->top; ++q->bottom) {
|
||||
if (q->queue[q->bottom % q->size]) {
|
||||
free(q->queue[q->bottom % q->size]);
|
||||
q->queue[q->bottom % q->size] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void jbuf_free(struct JitterBuffer *q)
|
||||
{
|
||||
if (!q) return;
|
||||
|
||||
jbuf_clear(q);
|
||||
free(q->queue);
|
||||
free(q);
|
||||
}
|
||||
static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m)
|
||||
{
|
||||
uint16_t sequnum = m->header.sequnum;
|
||||
|
||||
unsigned int num = sequnum % q->size;
|
||||
|
||||
if ((uint32_t)(sequnum - q->bottom) > q->size) {
|
||||
LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
|
||||
|
||||
jbuf_clear(q);
|
||||
q->bottom = sequnum - q->capacity;
|
||||
q->queue[num] = m;
|
||||
q->top = sequnum + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (q->queue[num])
|
||||
return -1;
|
||||
|
||||
q->queue[num] = m;
|
||||
|
||||
if ((sequnum - q->bottom) >= (q->top - q->bottom))
|
||||
q->top = sequnum + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static struct RTPMessage *jbuf_read(struct 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]) {
|
||||
struct RTPMessage *ret = q->queue[num];
|
||||
q->queue[num] = NULL;
|
||||
++q->bottom;
|
||||
*success = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((uint32_t)(q->top - q->bottom) > q->capacity) {
|
||||
++q->bottom;
|
||||
*success = 2;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*success = 0;
|
||||
return NULL;
|
||||
}
|
||||
OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
|
||||
{
|
||||
int status = OPUS_OK;
|
||||
OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
/* Enable in-band forward error correction in codec */
|
||||
status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1));
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
/* Make codec resistant to up to 10% packet loss
|
||||
* NOTE This could also be adjusted on the fly, rather than hard-coded,
|
||||
* with feedback from the receiving client.
|
||||
*/
|
||||
status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10));
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
/* Set algorithm to the highest complexity, maximizing compression */
|
||||
status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
FAILURE:
|
||||
opus_encoder_destroy(rc);
|
||||
return NULL;
|
||||
}
|
||||
bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
|
||||
int32_t *old_br, int32_t *old_sr, int32_t *old_ch)
|
||||
{
|
||||
/* Values are checked in toxav.c */
|
||||
if (*old_sr != new_sr || *old_ch != new_ch) {
|
||||
OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
|
||||
|
||||
if (new_encoder == NULL)
|
||||
return false;
|
||||
|
||||
opus_encoder_destroy(*e);
|
||||
*e = new_encoder;
|
||||
} else if (*old_br == new_br)
|
||||
return true; /* Nothing changed */
|
||||
else {
|
||||
int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*old_br = new_br;
|
||||
*old_sr = new_sr;
|
||||
*old_ch = new_ch;
|
||||
|
||||
LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", new_br, new_sr, new_ch);
|
||||
return true;
|
||||
}
|
||||
bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels)
|
||||
{
|
||||
if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) {
|
||||
if (current_time_monotonic() - ac->ldrts < 500)
|
||||
return false;
|
||||
|
||||
int status;
|
||||
OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status);
|
||||
|
||||
if (status != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
ac->ld_sample_rate = sampling_rate;
|
||||
ac->ld_channel_count = channels;
|
||||
ac->ldrts = current_time_monotonic();
|
||||
|
||||
opus_decoder_destroy(ac->decoder);
|
||||
ac->decoder = new_dec;
|
||||
|
||||
LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
64
toxav/audio.h
Normal file
64
toxav/audio.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/** audio.h
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <opus.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "toxav.h"
|
||||
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
struct RTPMessage;
|
||||
|
||||
typedef struct ACSession_s {
|
||||
/* encoding */
|
||||
OpusEncoder *encoder;
|
||||
int32_t le_sample_rate; /* Last encoder sample rate */
|
||||
int32_t le_channel_count; /* Last encoder channel count */
|
||||
int32_t le_bit_rate; /* Last encoder bit rate */
|
||||
|
||||
/* decoding */
|
||||
OpusDecoder *decoder;
|
||||
int32_t lp_channel_count; /* Last packet channel count */
|
||||
int32_t lp_sampling_rate; /* Last packet sample rate */
|
||||
int32_t lp_frame_duration; /* Last packet frame duration */
|
||||
int32_t ld_sample_rate; /* Last decoder sample rate */
|
||||
int32_t ld_channel_count; /* Last decoder channel count */
|
||||
uint64_t ldrts; /* Last decoder reconfiguration time stamp */
|
||||
void *j_buf;
|
||||
|
||||
pthread_mutex_t queue_mutex[1];
|
||||
|
||||
ToxAV *av;
|
||||
uint32_t friend_number;
|
||||
PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
|
||||
} ACSession;
|
||||
|
||||
ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
|
||||
void ac_kill(ACSession *ac);
|
||||
void ac_iterate(ACSession *ac);
|
||||
int ac_queue_message(void *acp, struct RTPMessage *msg);
|
||||
int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
|
||||
|
||||
#endif /* AUDIO_H */
|
205
toxav/bwcontroler.c
Normal file
205
toxav/bwcontroler.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
/** bwcontroler.c
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <assert.h>
|
||||
#include "bwcontroler.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
#define BWC_PACKET_ID 196
|
||||
#define BWC_SEND_INTERVAL_MS 1000
|
||||
#define BWC_REFRESH_INTERVAL_MS 10000
|
||||
#define BWC_AVG_PKT_COUNT 20
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
struct BWControler_s {
|
||||
void (*mcb) (BWControler *, uint32_t, float, void *);
|
||||
void *mcb_data;
|
||||
|
||||
Messenger *m;
|
||||
uint32_t friend_number;
|
||||
|
||||
struct {
|
||||
uint32_t lru; /* Last recv update time stamp */
|
||||
uint32_t lsu; /* Last sent update time stamp */
|
||||
uint32_t lfu; /* Last refresh time stamp */
|
||||
|
||||
uint32_t lost;
|
||||
uint32_t recv;
|
||||
} cycle;
|
||||
|
||||
struct {
|
||||
uint32_t rb_s[BWC_AVG_PKT_COUNT];
|
||||
RingBuffer *rb;
|
||||
} rcvpkt; /* To calculate average received packet */
|
||||
};
|
||||
|
||||
int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
|
||||
void send_update(BWControler *bwc);
|
||||
|
||||
BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
|
||||
void (*mcb) (BWControler *, uint32_t, float, void *),
|
||||
void *udata)
|
||||
{
|
||||
BWControler *retu = calloc(sizeof(struct BWControler_s), 1);
|
||||
|
||||
retu->mcb = mcb;
|
||||
retu->mcb_data = udata;
|
||||
retu->m = m;
|
||||
retu->friend_number = friendnumber;
|
||||
retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic();
|
||||
retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
|
||||
|
||||
/* Fill with zeros */
|
||||
int i = 0;
|
||||
|
||||
for (; i < BWC_AVG_PKT_COUNT; i ++)
|
||||
rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
|
||||
|
||||
m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
|
||||
|
||||
return retu;
|
||||
}
|
||||
void bwc_kill(BWControler *bwc)
|
||||
{
|
||||
if (!bwc)
|
||||
return;
|
||||
|
||||
m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
|
||||
|
||||
rb_kill(bwc->rcvpkt.rb);
|
||||
free(bwc);
|
||||
}
|
||||
void bwc_feed_avg(BWControler *bwc, uint32_t bytes)
|
||||
{
|
||||
uint32_t *p;
|
||||
|
||||
rb_read(bwc->rcvpkt.rb, (void **) &p);
|
||||
rb_write(bwc->rcvpkt.rb, p);
|
||||
|
||||
*p = bytes;
|
||||
}
|
||||
void bwc_add_lost(BWControler *bwc, uint32_t bytes)
|
||||
{
|
||||
if (!bwc)
|
||||
return;
|
||||
|
||||
if (!bytes) {
|
||||
uint32_t *t_avg[BWC_AVG_PKT_COUNT], c = 1;
|
||||
|
||||
rb_data(bwc->rcvpkt.rb, (void **) t_avg);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < BWC_AVG_PKT_COUNT; i ++) {
|
||||
bytes += *(t_avg[i]);
|
||||
|
||||
if (*(t_avg[i]))
|
||||
c++;
|
||||
}
|
||||
|
||||
bytes /= c;
|
||||
}
|
||||
|
||||
bwc->cycle.lost += bytes;
|
||||
send_update(bwc);
|
||||
}
|
||||
void bwc_add_recv(BWControler *bwc, uint32_t bytes)
|
||||
{
|
||||
if (!bwc || !bytes)
|
||||
return;
|
||||
|
||||
bwc->cycle.recv += bytes;
|
||||
send_update(bwc);
|
||||
}
|
||||
|
||||
|
||||
struct BWCMessage {
|
||||
uint32_t lost;
|
||||
uint32_t recv;
|
||||
};
|
||||
|
||||
void send_update(BWControler *bwc)
|
||||
{
|
||||
if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
|
||||
|
||||
bwc->cycle.lost /= 10;
|
||||
bwc->cycle.recv /= 10;
|
||||
bwc->cycle.lfu = current_time_monotonic();
|
||||
} else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
|
||||
|
||||
if (bwc->cycle.lost) {
|
||||
LOGGER_DEBUG ("%p Sent update rcv: %u lost: %u",
|
||||
bwc, bwc->cycle.recv, bwc->cycle.lost);
|
||||
|
||||
uint8_t p_msg[sizeof(struct BWCMessage) + 1];
|
||||
struct BWCMessage *b_msg = (struct BWCMessage *)(p_msg + 1);
|
||||
|
||||
p_msg[0] = BWC_PACKET_ID;
|
||||
b_msg->lost = htonl(bwc->cycle.lost);
|
||||
b_msg->recv = htonl(bwc->cycle.recv);
|
||||
|
||||
if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, p_msg, sizeof(p_msg)))
|
||||
LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(p_msg), strerror(errno));
|
||||
}
|
||||
|
||||
bwc->cycle.lsu = current_time_monotonic();
|
||||
}
|
||||
}
|
||||
int on_update (BWControler *bwc, struct BWCMessage *msg)
|
||||
{
|
||||
LOGGER_DEBUG ("%p Got update from peer", bwc);
|
||||
|
||||
/* Peer must respect time boundary */
|
||||
if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) {
|
||||
LOGGER_DEBUG("%p Rejecting extra update", bwc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bwc->cycle.lru = current_time_monotonic();
|
||||
|
||||
msg->recv = ntohl(msg->recv);
|
||||
msg->lost = ntohl(msg->lost);
|
||||
|
||||
LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost);
|
||||
|
||||
if (msg->lost && bwc->mcb)
|
||||
bwc->mcb(bwc, bwc->friend_number,
|
||||
((float) (msg->lost) / (msg->recv + msg->lost)),
|
||||
bwc->mcb_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
|
||||
{
|
||||
if (length - 1 != sizeof(struct BWCMessage))
|
||||
return -1;
|
||||
|
||||
/* NOTE the data is mutable */
|
||||
return on_update(object, (struct BWCMessage *) (data + 1));
|
||||
}
|
37
toxav/bwcontroler.h
Normal file
37
toxav/bwcontroler.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/** bwcontroler.h
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BWCONROLER_H
|
||||
#define BWCONROLER_H
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
typedef struct BWControler_s BWControler;
|
||||
|
||||
BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
|
||||
void (*mcb) (BWControler *, uint32_t, float, void *),
|
||||
void *udata);
|
||||
void bwc_kill(BWControler *bwc);
|
||||
|
||||
void bwc_feed_avg(BWControler *bwc, uint32_t bytes);
|
||||
void bwc_add_lost(BWControler *bwc, uint32_t bytes);
|
||||
void bwc_add_recv(BWControler *bwc, uint32_t bytes);
|
||||
|
||||
#endif /* BWCONROLER_H */
|
705
toxav/codec.c
705
toxav/codec.c
|
@ -1,705 +0,0 @@
|
|||
/** codec.c
|
||||
*
|
||||
* Audio and video codec intitialization, encoding/decoding and playback
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include "codec.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "msi.h"
|
||||
#include "rtp.h"
|
||||
|
||||
/* Good quality encode. */
|
||||
#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[]; }
|
||||
|
||||
typedef ARRAY(uint8_t) Payload;
|
||||
|
||||
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 * 4)) {
|
||||
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 jbuf_clear(JitterBuffer *q)
|
||||
{
|
||||
for (; q->bottom != q->top; ++q->bottom) {
|
||||
if (q->queue[q->bottom % q->size]) {
|
||||
rtp_free_msg(NULL, q->queue[q->bottom % q->size]);
|
||||
q->queue[q->bottom % q->size] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void jbuf_free(JitterBuffer *q)
|
||||
{
|
||||
if (!q) return;
|
||||
|
||||
jbuf_clear(q);
|
||||
free(q->queue);
|
||||
free(q);
|
||||
}
|
||||
|
||||
static int jbuf_write(JitterBuffer *q, RTPMessage *m)
|
||||
{
|
||||
uint16_t sequnum = m->header->sequnum;
|
||||
|
||||
unsigned int num = sequnum % q->size;
|
||||
|
||||
if ((uint32_t)(sequnum - q->bottom) > q->size) {
|
||||
jbuf_clear(q);
|
||||
q->bottom = sequnum - q->capacity;
|
||||
q->queue[num] = m;
|
||||
q->top = sequnum + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (q->queue[num])
|
||||
return -1;
|
||||
|
||||
q->queue[num] = m;
|
||||
|
||||
if ((sequnum - q->bottom) >= (q->top - q->bottom))
|
||||
q->top = sequnum + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
++q->bottom;
|
||||
*success = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((uint32_t)(q->top - q->bottom) > q->capacity) {
|
||||
++q->bottom;
|
||||
*success = 2;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*success = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_audio_decoder(CSSession *cs)
|
||||
{
|
||||
int 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = 48;
|
||||
cfg.kf_mode = VPX_KF_AUTO;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cs->max_width = max_width;
|
||||
cs->max_height = max_height;
|
||||
cs->video_bitrate = video_bitrate;
|
||||
|
||||
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_VOIP, &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;
|
||||
}
|
||||
|
||||
/* Enable in-band forward error correction in codec */
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_INBAND_FEC(1));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make codec resistant to up to 10% packet loss */
|
||||
rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_PACKET_LOSS_PERC(10));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set algorithm to the highest complexity, maximizing compression */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 cs_ErrorSplittingVideoPayload;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* Codec session should always be protected by call mutex so no need to check for cs validity
|
||||
*/
|
||||
|
||||
if (!cs) return;
|
||||
|
||||
Payload *p;
|
||||
int rc;
|
||||
|
||||
int success = 0;
|
||||
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
RTPMessage *msg;
|
||||
|
||||
while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
|
||||
uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
|
||||
int16_t tmp[fsize * cs->audio_decoder_channels];
|
||||
|
||||
if (success == 2) {
|
||||
rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1);
|
||||
} else {
|
||||
rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0);
|
||||
rtp_free_msg(NULL, msg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
|
||||
} else if (cs->acb.first) {
|
||||
/* Play */
|
||||
cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
}
|
||||
|
||||
if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
|
||||
/* Decode video */
|
||||
buffer_read(cs->vbuf_raw, &p);
|
||||
|
||||
/* Leave space for (possibly) other thread to queue more data after we read it here */
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
|
||||
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)) {
|
||||
if (cs->vcb.first)
|
||||
cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second);
|
||||
|
||||
vpx_img_free(dest);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (cfg.g_w == width && cfg.g_h == height)
|
||||
return 0;
|
||||
|
||||
if (width * height > cs->max_width * cs->max_height) {
|
||||
vpx_codec_ctx_t v_encoder = cs->v_encoder;
|
||||
|
||||
if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) {
|
||||
cs->v_encoder = v_encoder;
|
||||
return cs_ErrorSettingVideoResolution;
|
||||
}
|
||||
|
||||
vpx_codec_destroy(&v_encoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("New video resolution: %u %u", width, height);
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
return cs_ErrorSettingVideoResolution;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
|
||||
{
|
||||
vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
|
||||
|
||||
if (cfg.rc_target_bitrate == video_bitrate)
|
||||
return 0;
|
||||
|
||||
LOGGER_DEBUG("New video bitrate: %u", video_bitrate);
|
||||
cfg.rc_target_bitrate = video_bitrate;
|
||||
int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
|
||||
|
||||
if ( rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
return cs_ErrorSettingVideoBitrate;
|
||||
}
|
||||
|
||||
cs->video_bitrate = video_bitrate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
|
||||
{
|
||||
CSSession *cs = calloc(sizeof(CSSession), 1);
|
||||
|
||||
if (!cs) {
|
||||
LOGGER_WARNING("Allocation failed! Application might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(cs->queue_mutex) != 0) {
|
||||
LOGGER_WARNING("Failed to create recursive mutex!");
|
||||
free(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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) ) ? cs_AudioEncoding : 0;
|
||||
cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0;
|
||||
|
||||
if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) 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) ) ? cs_VideoEncoding : 0;
|
||||
cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0;
|
||||
|
||||
if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) 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;
|
||||
}
|
||||
|
||||
return cs;
|
||||
|
||||
error:
|
||||
LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
|
||||
|
||||
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 (has_video) {
|
||||
if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder);
|
||||
|
||||
if ( cs->capabilities & cs_VideoEncoding ) 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 cs_kill(CSSession *cs)
|
||||
{
|
||||
if (!cs) return;
|
||||
|
||||
/* queue_message will not be called since it's unregistered before cs_kill is called */
|
||||
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 & cs_VideoDecoding )
|
||||
vpx_codec_destroy(&cs->v_decoder);
|
||||
|
||||
if ( cs->capabilities & cs_VideoEncoding )
|
||||
vpx_codec_destroy(&cs->v_encoder);
|
||||
|
||||
jbuf_free(cs->j_buf);
|
||||
buffer_free(cs->vbuf_raw);
|
||||
free(cs->frame_buf);
|
||||
free(cs->split_video_frame);
|
||||
|
||||
LOGGER_DEBUG("Terminated codec state: %p", cs);
|
||||
free(cs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Called from RTP */
|
||||
void queue_message(RTPSession *session, RTPMessage *msg)
|
||||
{
|
||||
/* This function is unregistered during call termination befor destroing
|
||||
* Codec session so no need to check for validity of cs
|
||||
*/
|
||||
CSSession *cs = session->cs;
|
||||
|
||||
if (!cs) return;
|
||||
|
||||
/* Audio */
|
||||
if (session->payload_type == msi_TypeAudio % 128) {
|
||||
pthread_mutex_lock(cs->queue_mutex);
|
||||
int ret = jbuf_write(cs->j_buf, msg);
|
||||
pthread_mutex_unlock(cs->queue_mutex);
|
||||
|
||||
if (ret == -1) {
|
||||
rtp_free_msg(NULL, msg);
|
||||
}
|
||||
}
|
||||
/* Video */
|
||||
else {
|
||||
uint8_t *packet = msg->data;
|
||||
uint32_t packet_size = msg->length;
|
||||
|
||||
if (packet_size < VIDEOFRAME_HEADER_SIZE)
|
||||
goto end;
|
||||
|
||||
uint8_t diff = packet[0] - cs->frameid_in;
|
||||
|
||||
if (diff != 0) {
|
||||
if (diff < 225) { /* New frame */
|
||||
/* Flush last frames' data and get ready for this frame */
|
||||
Payload *p = malloc(sizeof(Payload) + 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 { /* Old frame; drop */
|
||||
LOGGER_DEBUG("Old packet: %u", packet[0]);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t piece_number = packet[1];
|
||||
|
||||
uint32_t length_before_piece = ((piece_number - 1) * cs->video_frame_piece_size);
|
||||
uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
|
||||
|
||||
if (framebuf_new_length > cs->max_video_frame_size) {
|
||||
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 + length_before_piece,
|
||||
packet + VIDEOFRAME_HEADER_SIZE,
|
||||
packet_size - VIDEOFRAME_HEADER_SIZE);
|
||||
|
||||
if (framebuf_new_length > cs->frame_size) {
|
||||
cs->frame_size = framebuf_new_length;
|
||||
}
|
||||
|
||||
end:
|
||||
rtp_free_msg(NULL, msg);
|
||||
}
|
||||
}
|
176
toxav/codec.h
176
toxav/codec.h
|
@ -1,176 +0,0 @@
|
|||
/** codec.h
|
||||
*
|
||||
* Audio and video codec intitialization, encoding/decoding and playback
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CODEC_H_
|
||||
#define _CODEC_H_
|
||||
|
||||
#include "toxav.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <vpx/vpx_decoder.h>
|
||||
#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>
|
||||
|
||||
#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Codec capabilities
|
||||
*/
|
||||
typedef enum {
|
||||
cs_AudioEncoding = 1 << 0,
|
||||
cs_AudioDecoding = 1 << 1,
|
||||
cs_VideoEncoding = 1 << 2,
|
||||
cs_VideoDecoding = 1 << 3
|
||||
} CSCapabilities;
|
||||
|
||||
/**
|
||||
* Codec errors.
|
||||
*/
|
||||
typedef enum {
|
||||
cs_ErrorSettingVideoResolution = -30,
|
||||
cs_ErrorSettingVideoBitrate = -31,
|
||||
cs_ErrorSplittingVideoPayload = -32,
|
||||
} CSError;
|
||||
|
||||
/**
|
||||
* Codec session - controling codec
|
||||
*/
|
||||
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 max_width;
|
||||
int max_height;
|
||||
unsigned int video_bitrate;
|
||||
|
||||
|
||||
/* 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_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;
|
||||
|
||||
struct _JitterBuffer *j_buf;
|
||||
|
||||
|
||||
/* Voice activity detection */
|
||||
uint32_t EVAD_tolerance; /* In frames */
|
||||
uint32_t EVAD_tolerance_cr;
|
||||
|
||||
|
||||
|
||||
/* OTHER
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
uint64_t capabilities; /* supports*/
|
||||
|
||||
/* Callbacks */
|
||||
PAIR(CSAudioCallback, void *) acb;
|
||||
PAIR(CSVideoCallback, void *) vcb;
|
||||
|
||||
/* Buffering */
|
||||
void *vbuf_raw; /* Un-decoded data */
|
||||
pthread_mutex_t queue_mutex[1];
|
||||
|
||||
void *agent; /* Pointer to ToxAv */
|
||||
int32_t call_idx;
|
||||
} CSSession;
|
||||
|
||||
/* Make sure to be called BEFORE corresponding rtp_new */
|
||||
CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video);
|
||||
/* Make sure to be called AFTER corresponding rtp_kill */
|
||||
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);
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
/* Internal. Called from rtp_handle_message */
|
||||
void queue_message(RTPSession *session, RTPMessage *msg);
|
||||
#endif /* _CODEC_H_ */
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include "group.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity)
|
|||
|
||||
Group_JitterBuffer *q;
|
||||
|
||||
if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL;
|
||||
if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL;
|
||||
|
||||
if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) {
|
||||
free(q);
|
||||
|
@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av)
|
|||
group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
|
||||
OPUS_APPLICATION_AUDIO, &rc);
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
if (rc != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
|
||||
group_av->audio_encoder = NULL;
|
||||
return -1;
|
||||
|
@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av)
|
|||
|
||||
rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
if (rc != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
opus_encoder_destroy(group_av->audio_encoder);
|
||||
group_av->audio_encoder = NULL;
|
||||
|
@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av)
|
|||
|
||||
rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
if (rc != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
|
||||
opus_encoder_destroy(group_av->audio_encoder);
|
||||
group_av->audio_encoder = NULL;
|
||||
|
@ -306,7 +306,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int g
|
|||
int rc;
|
||||
peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
|
||||
|
||||
if ( rc != OPUS_OK ) {
|
||||
if (rc != OPUS_OK) {
|
||||
LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
|
||||
free(pk);
|
||||
return -1;
|
||||
|
|
2014
toxav/msi.c
2014
toxav/msi.c
File diff suppressed because it is too large
Load Diff
193
toxav/msi.h
193
toxav/msi.h
|
@ -1,6 +1,6 @@
|
|||
/** msi.h
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
|
@ -19,184 +19,133 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __TOXMSI
|
||||
#define __TOXMSI
|
||||
#ifndef MSI_H
|
||||
#define MSI_H
|
||||
|
||||
#include "codec.h"
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "video.h"
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
typedef uint8_t MSICallIDType[12];
|
||||
typedef uint8_t MSIReasonStrType[255];
|
||||
typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg );
|
||||
|
||||
/**
|
||||
* Call type identifier. Also used as rtp callback prefix.
|
||||
* Error codes.
|
||||
*/
|
||||
typedef enum {
|
||||
msi_TypeAudio = 192,
|
||||
msi_TypeVideo
|
||||
} MSICallType;
|
||||
msi_ENone,
|
||||
msi_EInvalidMessage,
|
||||
msi_EInvalidParam,
|
||||
msi_EInvalidState,
|
||||
msi_EStrayMessage,
|
||||
msi_ESystem,
|
||||
msi_EHandle,
|
||||
msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing will not work */
|
||||
} MSIError;
|
||||
|
||||
/**
|
||||
* Supported capabilities
|
||||
*/
|
||||
typedef enum {
|
||||
msi_CapSAudio = 4, /* sending audio */
|
||||
msi_CapSVideo = 8, /* sending video */
|
||||
msi_CapRAudio = 16, /* receiving audio */
|
||||
msi_CapRVideo = 32, /* receiving video */
|
||||
} MSICapabilities;
|
||||
|
||||
|
||||
/**
|
||||
* Call state identifiers.
|
||||
*/
|
||||
typedef enum {
|
||||
msi_CallInviting, /* when sending call invite */
|
||||
msi_CallStarting, /* when getting call invite */
|
||||
msi_CallInactive, /* Default */
|
||||
msi_CallActive,
|
||||
msi_CallHold,
|
||||
msi_CallOver
|
||||
|
||||
msi_CallRequesting, /* when sending call invite */
|
||||
msi_CallRequested, /* when getting call invite */
|
||||
} MSICallState;
|
||||
|
||||
|
||||
/**
|
||||
* Encoding settings.
|
||||
*/
|
||||
typedef struct _MSICodecSettings {
|
||||
MSICallType call_type;
|
||||
|
||||
uint32_t video_bitrate; /* In kbits/s */
|
||||
uint16_t max_video_width; /* In px */
|
||||
uint16_t max_video_height; /* In px */
|
||||
|
||||
uint32_t audio_bitrate; /* In bits/s */
|
||||
uint16_t audio_frame_duration; /* In ms */
|
||||
uint32_t audio_sample_rate; /* In Hz */
|
||||
uint32_t audio_channels;
|
||||
} MSICSettings;
|
||||
|
||||
|
||||
/**
|
||||
* Callbacks ids that handle the states
|
||||
*/
|
||||
typedef enum {
|
||||
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_OnError, /* On protocol error */
|
||||
msi_OnPeerTimeout, /* Peer timed out; stop the call */
|
||||
msi_OnPeerCSChange, /* Peer requested Csettings change */
|
||||
msi_OnSelfCSChange /* Csettings change confirmation */
|
||||
msi_OnCapabilities, /* Peer requested capabilities change */
|
||||
} MSICallbackID;
|
||||
|
||||
/**
|
||||
* Errors
|
||||
* The call struct. Please do not modify outside msi.c
|
||||
*/
|
||||
typedef enum {
|
||||
msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
|
||||
msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
|
||||
msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
|
||||
msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
|
||||
} MSIError;
|
||||
typedef struct MSICall_s {
|
||||
struct MSISession_s *session; /* Session pointer */
|
||||
|
||||
/**
|
||||
* The call struct.
|
||||
*/
|
||||
typedef struct _MSICall { /* Call info structure */
|
||||
struct _MSISession *session; /* Session pointer */
|
||||
MSICallState state;
|
||||
uint8_t peer_capabilities; /* Peer capabilities */
|
||||
uint8_t self_capabilities; /* Self capabilities */
|
||||
uint16_t peer_vfpsz; /* Video frame piece size */
|
||||
uint32_t friend_number; /* Index of this call in MSISession */
|
||||
MSIError error; /* Last error */
|
||||
|
||||
MSICallState state;
|
||||
void *av_call; /* Pointer to av call handler */
|
||||
|
||||
MSICSettings csettings_local; /* Local call settings */
|
||||
MSICSettings *csettings_peer; /* Peers call settings */
|
||||
|
||||
MSICallIDType id; /* Random value identifying the call */
|
||||
|
||||
int ringing_tout_ms; /* Ringing timeout in ms */
|
||||
|
||||
int request_timer_id; /* Timer id for outgoing request/action */
|
||||
int ringing_timer_id; /* Timer id for ringing timeout */
|
||||
|
||||
uint32_t *peers;
|
||||
uint16_t peer_count;
|
||||
|
||||
int32_t call_idx; /* Index of this call in MSISession */
|
||||
struct MSICall_s *next;
|
||||
struct MSICall_s *prev;
|
||||
} MSICall;
|
||||
|
||||
|
||||
/**
|
||||
* Control session struct
|
||||
* Expected return on success is 0, if any other number is
|
||||
* returned the call is considered errored and will be handled
|
||||
* as such which means it will be terminated without any notice.
|
||||
*/
|
||||
typedef struct _MSISession {
|
||||
typedef int msi_action_cb (void *av, MSICall *call);
|
||||
|
||||
/**
|
||||
* Control session struct. Please do not modify outside msi.c
|
||||
*/
|
||||
typedef struct MSISession_s {
|
||||
/* Call handlers */
|
||||
MSICall **calls;
|
||||
int32_t max_calls;
|
||||
uint32_t calls_tail;
|
||||
uint32_t calls_head;
|
||||
|
||||
void *agent_handler;
|
||||
Messenger *messenger_handle;
|
||||
|
||||
uint32_t frequ;
|
||||
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
|
||||
void *av;
|
||||
Messenger *messenger;
|
||||
|
||||
pthread_mutex_t mutex[1];
|
||||
|
||||
void *timer_handler;
|
||||
PAIR(MSICallbackType, void *) callbacks[10];
|
||||
msi_action_cb *callbacks[7];
|
||||
} MSISession;
|
||||
|
||||
/**
|
||||
* Start the control session.
|
||||
*/
|
||||
MSISession *msi_new ( Messenger *messenger, int32_t max_calls );
|
||||
|
||||
MSISession *msi_new(Messenger *m);
|
||||
/**
|
||||
* Terminate control session.
|
||||
* Terminate control session. NOTE: all calls will be freed
|
||||
*/
|
||||
int msi_kill ( MSISession *session );
|
||||
|
||||
int msi_kill(MSISession *session);
|
||||
/**
|
||||
* Callback setter.
|
||||
*/
|
||||
void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata);
|
||||
|
||||
void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id);
|
||||
/**
|
||||
* Send invite request to friend_id.
|
||||
* Send invite request to friend_number.
|
||||
*/
|
||||
int msi_invite ( MSISession *session,
|
||||
int32_t *call_index,
|
||||
const MSICSettings *csettings,
|
||||
uint32_t rngsec,
|
||||
uint32_t friend_id );
|
||||
|
||||
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
|
||||
/**
|
||||
* Hangup active call.
|
||||
* Hangup call. NOTE: 'call' will be freed
|
||||
*/
|
||||
int msi_hangup ( MSISession *session, int32_t call_index );
|
||||
|
||||
int msi_hangup(MSICall *call);
|
||||
/**
|
||||
* Answer active call request.
|
||||
* Answer call request.
|
||||
*/
|
||||
int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
|
||||
|
||||
int msi_answer(MSICall *call, uint8_t capabilities);
|
||||
/**
|
||||
* Cancel request.
|
||||
* Change capabilities of the call.
|
||||
*/
|
||||
int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason );
|
||||
int msi_change_capabilities(MSICall *call, uint8_t capabilities);
|
||||
|
||||
/**
|
||||
* Reject incoming call.
|
||||
*/
|
||||
int msi_reject ( MSISession *session, int32_t call_index, const char *reason );
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
#endif /* MSI_H */
|
||||
|
|
721
toxav/rtp.c
721
toxav/rtp.c
|
@ -1,6 +1,6 @@
|
|||
/** rtp.c
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
|
@ -24,505 +24,370 @@
|
|||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include "rtp.h"
|
||||
#include "bwcontroler.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
void queue_message(RTPSession *_session, RTPMessage *_msg);
|
||||
#include <assert.h>
|
||||
|
||||
#define size_32 4
|
||||
|
||||
#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
|
||||
#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
|
||||
#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
|
||||
#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
|
||||
#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
|
||||
#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
|
||||
int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
|
||||
|
||||
#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
|
||||
#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
|
||||
#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
|
||||
#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
|
||||
#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
|
||||
#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
|
||||
|
||||
/**
|
||||
* Checks if message came in late.
|
||||
*/
|
||||
static int check_late_message (RTPSession *session, RTPMessage *msg)
|
||||
RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
|
||||
BWControler *bwc, void *cs,
|
||||
int (*mcb) (void *, struct RTPMessage *))
|
||||
{
|
||||
/*
|
||||
* Check Sequence number. If this new msg has lesser number then the session->rsequnum
|
||||
* it shows that the message came in late. Also check timestamp to be 100% certain.
|
||||
*
|
||||
*/
|
||||
return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
|
||||
}
|
||||
assert(mcb);
|
||||
assert(cs);
|
||||
assert(m);
|
||||
|
||||
RTPSession *retu = calloc(1, sizeof(RTPSession));
|
||||
|
||||
/**
|
||||
* Extracts header from payload.
|
||||
*/
|
||||
RTPHeader *extract_header ( const uint8_t *payload, int length )
|
||||
{
|
||||
if ( !payload || !length ) {
|
||||
LOGGER_WARNING("No payload to extract!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RTPHeader *retu = calloc(1, sizeof (RTPHeader));
|
||||
|
||||
if ( !retu ) {
|
||||
if (!retu) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&retu->sequnum, payload, sizeof(retu->sequnum));
|
||||
retu->sequnum = ntohs(retu->sequnum);
|
||||
retu->ssrc = random_int();
|
||||
retu->payload_type = payload_type;
|
||||
|
||||
const uint8_t *it = payload + 2;
|
||||
retu->m = m;
|
||||
retu->friend_number = friendnumber;
|
||||
|
||||
retu->flags = *it;
|
||||
++it;
|
||||
/* Also set payload type as prefix */
|
||||
|
||||
/* This indicates if the first 2 bits are valid.
|
||||
* Now it may happen that this is out of order but
|
||||
* it cuts down chances of parsing some invalid value
|
||||
*/
|
||||
retu->bwc = bwc;
|
||||
retu->cs = cs;
|
||||
retu->mcb = mcb;
|
||||
|
||||
if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
|
||||
/* Deallocate */
|
||||
LOGGER_WARNING("Invalid version!");
|
||||
if (-1 == rtp_allow_receiving(retu)) {
|
||||
LOGGER_WARNING("Failed to start rtp receiving mode");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Added a check for the size of the header little sooner so
|
||||
* I don't need to parse the other stuff if it's bad
|
||||
*/
|
||||
uint8_t cc = GET_FLAG_CSRCC ( retu );
|
||||
int total = 12 /* Minimum header len */ + ( cc * 4 );
|
||||
|
||||
if ( length < total ) {
|
||||
/* Deallocate */
|
||||
LOGGER_WARNING("Length invalid!");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(retu->csrc, 0, 16 * sizeof (uint32_t));
|
||||
|
||||
retu->marker_payloadt = *it;
|
||||
++it;
|
||||
retu->length = total;
|
||||
|
||||
|
||||
memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
|
||||
retu->timestamp = ntohl(retu->timestamp);
|
||||
it += 4;
|
||||
memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
|
||||
retu->ssrc = ntohl(retu->ssrc);
|
||||
|
||||
uint8_t x;
|
||||
|
||||
for ( x = 0; x < cc; x++ ) {
|
||||
it += 4;
|
||||
memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
|
||||
retu->csrc[x] = ntohl(retu->csrc[x]);
|
||||
}
|
||||
|
||||
return retu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts external header from payload. Must be called AFTER extract_header()!
|
||||
*/
|
||||
RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
|
||||
void rtp_kill (RTPSession *session)
|
||||
{
|
||||
const uint8_t *it = payload;
|
||||
if (!session)
|
||||
return;
|
||||
|
||||
RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader));
|
||||
LOGGER_DEBUG("Terminated RTP session: %p", session);
|
||||
|
||||
if ( !retu ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t ext_length;
|
||||
memcpy(&ext_length, it, sizeof(ext_length));
|
||||
ext_length = ntohs(ext_length);
|
||||
it += 2;
|
||||
|
||||
|
||||
if ( length < ( ext_length * sizeof(uint32_t) ) ) {
|
||||
LOGGER_WARNING("Length invalid!");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retu->length = ext_length;
|
||||
memcpy(&retu->type, it, sizeof(retu->type));
|
||||
retu->type = ntohs(retu->type);
|
||||
it += 2;
|
||||
|
||||
if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t x;
|
||||
|
||||
for ( x = 0; x < ext_length; x++ ) {
|
||||
it += 4;
|
||||
memcpy(&(retu->table[x]), it, sizeof(retu->table[x]));
|
||||
retu->table[x] = ntohl(retu->table[x]);
|
||||
}
|
||||
|
||||
return retu;
|
||||
rtp_stop_receiving (session);
|
||||
free (session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds header to payload. Make sure _payload_ has enough space.
|
||||
*/
|
||||
uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
|
||||
int rtp_allow_receiving(RTPSession *session)
|
||||
{
|
||||
uint8_t cc = GET_FLAG_CSRCC ( header );
|
||||
uint8_t *it = payload;
|
||||
uint16_t sequnum;
|
||||
uint32_t timestamp;
|
||||
uint32_t ssrc;
|
||||
uint32_t csrc;
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
/* Add sequence number first */
|
||||
sequnum = htons(header->sequnum);
|
||||
memcpy(it, &sequnum, sizeof(sequnum));
|
||||
it += 2;
|
||||
|
||||
*it = header->flags;
|
||||
++it;
|
||||
*it = header->marker_payloadt;
|
||||
++it;
|
||||
|
||||
|
||||
timestamp = htonl(header->timestamp);
|
||||
memcpy(it, ×tamp, sizeof(timestamp));
|
||||
it += 4;
|
||||
ssrc = htonl(header->ssrc);
|
||||
memcpy(it, &ssrc, sizeof(ssrc));
|
||||
|
||||
uint8_t x;
|
||||
|
||||
for ( x = 0; x < cc; x++ ) {
|
||||
it += 4;
|
||||
csrc = htonl(header->csrc[x]);
|
||||
memcpy(it, &csrc, sizeof(csrc));
|
||||
if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
|
||||
handle_rtp_packet, session) == -1) {
|
||||
LOGGER_WARNING("Failed to register rtp receive handler");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return it + 4;
|
||||
LOGGER_DEBUG("Started receiving on session: %p", session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extension header to payload. Make sure _payload_ has enough space.
|
||||
*/
|
||||
uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
|
||||
int rtp_stop_receiving(RTPSession *session)
|
||||
{
|
||||
uint8_t *it = payload;
|
||||
uint16_t length;
|
||||
uint16_t type;
|
||||
uint32_t entry;
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
length = htons(header->length);
|
||||
memcpy(it, &length, sizeof(length));
|
||||
it += 2;
|
||||
type = htons(header->type);
|
||||
memcpy(it, &type, sizeof(type));
|
||||
it -= 2; /* Return to 0 position */
|
||||
m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
|
||||
|
||||
if ( header->table ) {
|
||||
|
||||
uint16_t x;
|
||||
|
||||
for ( x = 0; x < header->length; x++ ) {
|
||||
it += 4;
|
||||
entry = htonl(header->table[x]);
|
||||
memcpy(it, &entry, sizeof(entry));
|
||||
}
|
||||
}
|
||||
|
||||
return it + 4;
|
||||
LOGGER_DEBUG("Stopped receiving on session: %p", session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds header from control session values.
|
||||
*/
|
||||
RTPHeader *build_header ( RTPSession *session )
|
||||
int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) );
|
||||
|
||||
if ( !retu ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
return NULL;
|
||||
if (!session) {
|
||||
LOGGER_WARNING("No session!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ADD_FLAG_VERSION ( retu, session->version );
|
||||
ADD_FLAG_PADDING ( retu, session->padding );
|
||||
ADD_FLAG_EXTENSION ( retu, session->extension );
|
||||
ADD_FLAG_CSRCC ( retu, session->cc );
|
||||
ADD_SETTING_MARKER ( retu, session->marker );
|
||||
ADD_SETTING_PAYLOAD ( retu, session->payload_type );
|
||||
uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
|
||||
memset(rdata, 0, sizeof(rdata));
|
||||
|
||||
retu->sequnum = session->sequnum;
|
||||
retu->timestamp = current_time_monotonic(); /* milliseconds */
|
||||
retu->ssrc = session->ssrc;
|
||||
rdata[0] = session->payload_type;
|
||||
|
||||
int i;
|
||||
struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
|
||||
|
||||
for ( i = 0; i < session->cc; i++ )
|
||||
retu->csrc[i] = session->csrc[i];
|
||||
header->ve = 2;
|
||||
header->pe = 0;
|
||||
header->xe = 0;
|
||||
header->cc = 0;
|
||||
|
||||
retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
|
||||
header->ma = 0;
|
||||
header->pt = session->payload_type % 128;
|
||||
|
||||
return retu;
|
||||
}
|
||||
header->sequnum = htons(session->sequnum);
|
||||
header->timestamp = htonl(current_time_monotonic());
|
||||
header->ssrc = htonl(session->ssrc);
|
||||
|
||||
header->cpart = 0;
|
||||
header->tlen = htons(length);
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
RTPMessage *retu = calloc(1, sizeof (RTPMessage));
|
||||
if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
|
||||
|
||||
retu->header = extract_header ( data, length ); /* It allocates memory and all */
|
||||
/**
|
||||
* The lenght is lesser than the maximum allowed lenght (including header)
|
||||
* Send the packet in single piece.
|
||||
*/
|
||||
|
||||
if ( !retu->header ) {
|
||||
LOGGER_WARNING("Header failed to extract!");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
|
||||
|
||||
uint16_t from_pos = retu->header->length;
|
||||
retu->length = length - from_pos;
|
||||
|
||||
|
||||
|
||||
if ( GET_FLAG_EXTENSION ( retu->header ) ) {
|
||||
retu->ext_header = extract_ext_header ( data + from_pos, length );
|
||||
|
||||
if ( retu->ext_header ) {
|
||||
retu->length -= ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
|
||||
from_pos += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
|
||||
} else { /* Error */
|
||||
LOGGER_WARNING("Ext Header failed to extract!");
|
||||
rtp_free_msg(NULL, retu);
|
||||
return NULL;
|
||||
}
|
||||
if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
|
||||
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
|
||||
} else {
|
||||
retu->ext_header = NULL;
|
||||
|
||||
/**
|
||||
* The lenght is greater than the maximum allowed lenght (including header)
|
||||
* Send the packet in multiple pieces.
|
||||
*/
|
||||
|
||||
uint16_t sent = 0;
|
||||
uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
|
||||
|
||||
while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
|
||||
memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
|
||||
|
||||
if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
|
||||
rdata, piece + sizeof(struct RTPHeader) + 1))
|
||||
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
|
||||
piece + sizeof(struct RTPHeader) + 1, strerror(errno));
|
||||
|
||||
sent += piece;
|
||||
header->cpart = htons(sent);
|
||||
}
|
||||
|
||||
/* Send remaining */
|
||||
piece = length - sent;
|
||||
|
||||
if (piece) {
|
||||
memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
|
||||
|
||||
if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
|
||||
piece + sizeof(struct RTPHeader) + 1))
|
||||
LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
|
||||
piece + sizeof(struct RTPHeader) + 1, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if ( length - from_pos <= MAX_RTP_SIZE )
|
||||
memcpy ( retu->data, data + from_pos, length - from_pos );
|
||||
else {
|
||||
LOGGER_WARNING("Invalid length!");
|
||||
rtp_free_msg(NULL, retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retu->next = NULL;
|
||||
|
||||
return retu;
|
||||
session->sequnum ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for networking core.
|
||||
*/
|
||||
int rtp_handle_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object )
|
||||
{
|
||||
RTPSession *session = object;
|
||||
RTPMessage *msg;
|
||||
|
||||
if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */
|
||||
bool chloss (const RTPSession *session, const struct RTPHeader *header)
|
||||
{
|
||||
if (ntohl(header->timestamp) < session->rtimestamp) {
|
||||
uint16_t hosq, lost = 0;
|
||||
|
||||
hosq = ntohs(header->sequnum);
|
||||
|
||||
lost = (hosq > session->rsequnum) ?
|
||||
(session->rsequnum + 65535) - hosq :
|
||||
session->rsequnum - hosq;
|
||||
|
||||
puts ("Lost packet");
|
||||
|
||||
while (lost --)
|
||||
bwc_add_lost(session->bwc , 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
|
||||
{
|
||||
assert(allocate_len >= data_length);
|
||||
|
||||
struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
|
||||
|
||||
msg->len = data_length - sizeof(struct RTPHeader);
|
||||
memcpy(&msg->header, data, data_length);
|
||||
|
||||
msg->header.sequnum = ntohs(msg->header.sequnum);
|
||||
msg->header.timestamp = ntohl(msg->header.timestamp);
|
||||
msg->header.ssrc = ntohl(msg->header.ssrc);
|
||||
|
||||
msg->header.cpart = ntohs(msg->header.cpart);
|
||||
msg->header.tlen = ntohs(msg->header.tlen);
|
||||
|
||||
return msg;
|
||||
}
|
||||
int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
|
||||
{
|
||||
(void) m;
|
||||
(void) friendnumber;
|
||||
|
||||
RTPSession *session = object;
|
||||
|
||||
data ++;
|
||||
length--;
|
||||
|
||||
if (!session || length < sizeof (struct RTPHeader)) {
|
||||
LOGGER_WARNING("No session or invalid length of received buffer!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = msg_parse ( data + 1, length - 1 );
|
||||
const struct RTPHeader *header = (struct RTPHeader *) data;
|
||||
|
||||
if ( !msg ) {
|
||||
LOGGER_WARNING("Could not parse message!");
|
||||
if (header->pt != session->payload_type % 128) {
|
||||
LOGGER_WARNING("Invalid payload type with the session");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if message came in late */
|
||||
if ( check_late_message(session, msg) < 0 ) { /* Not late */
|
||||
session->rsequnum = msg->header->sequnum;
|
||||
session->timestamp = msg->header->timestamp;
|
||||
if (ntohs(header->cpart) >= ntohs(header->tlen)) {
|
||||
/* Never allow this case to happen */
|
||||
return -1;
|
||||
}
|
||||
|
||||
queue_message(session, msg);
|
||||
bwc_feed_avg(session->bwc, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
|
||||
/* The message is sent in single part */
|
||||
|
||||
/**
|
||||
* Allocate message and store data there
|
||||
*/
|
||||
RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
|
||||
{
|
||||
if ( !session ) {
|
||||
LOGGER_WARNING("No session!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *from_pos;
|
||||
RTPMessage *retu = calloc(1, sizeof (RTPMessage));
|
||||
|
||||
if ( !retu ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sets header values and copies the extension header in retu */
|
||||
retu->header = build_header ( session ); /* It allocates memory and all */
|
||||
retu->ext_header = session->ext_header;
|
||||
|
||||
|
||||
uint32_t total_length = length + retu->header->length + 1;
|
||||
|
||||
retu->data[0] = session->prefix;
|
||||
|
||||
if ( retu->ext_header ) {
|
||||
total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
|
||||
|
||||
from_pos = add_header ( retu->header, retu->data + 1 );
|
||||
from_pos = add_ext_header ( retu->ext_header, from_pos + 1 );
|
||||
} else {
|
||||
from_pos = add_header ( retu->header, retu->data + 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the extension header into the message
|
||||
* Of course if any
|
||||
*/
|
||||
|
||||
/* Appends data on to retu->data */
|
||||
memcpy ( from_pos, data, length );
|
||||
|
||||
retu->length = total_length;
|
||||
|
||||
retu->next = NULL;
|
||||
|
||||
return retu;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 ) return -1;
|
||||
|
||||
int ret = send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length);
|
||||
|
||||
if ( 0 != ret) {
|
||||
LOGGER_WARNING("Failed to send full packet (len: %d)! error: %i", length, ret);
|
||||
rtp_free_msg ( session, msg );
|
||||
return rtp_ErrorSending;
|
||||
}
|
||||
|
||||
/* Set sequ number */
|
||||
session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1;
|
||||
rtp_free_msg ( session, msg );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
|
||||
{
|
||||
if ( !session ) {
|
||||
if ( msg->ext_header ) {
|
||||
free ( msg->ext_header->table );
|
||||
free ( msg->ext_header );
|
||||
/* Only allow messages which have arrived in order;
|
||||
* drop late messages
|
||||
*/
|
||||
if (chloss(session, header)) {
|
||||
return 0;
|
||||
} else {
|
||||
/* Message is not late; pick up the latest parameters */
|
||||
session->rsequnum = ntohs(header->sequnum);
|
||||
session->rtimestamp = ntohl(header->timestamp);
|
||||
}
|
||||
|
||||
bwc_add_recv(session->bwc, length);
|
||||
|
||||
/* Invoke processing of active multiparted message */
|
||||
if (session->mp) {
|
||||
if (session->mcb)
|
||||
session->mcb (session->cs, session->mp);
|
||||
else
|
||||
free(session->mp);
|
||||
|
||||
session->mp = NULL;
|
||||
}
|
||||
|
||||
/* The message came in the allowed time;
|
||||
* process it only if handler for the session is present.
|
||||
*/
|
||||
|
||||
if (!session->mcb)
|
||||
return 0;
|
||||
|
||||
return session->mcb (session->cs, new_message(length, data, length));
|
||||
} else {
|
||||
if ( msg->ext_header && session->ext_header != msg->ext_header ) {
|
||||
free ( msg->ext_header->table );
|
||||
free ( msg->ext_header );
|
||||
/* The message is sent in multiple parts */
|
||||
|
||||
if (session->mp) {
|
||||
/* There are 2 possible situations in this case:
|
||||
* 1) being that we got the part of already processing message.
|
||||
* 2) being that we got the part of a new/old message.
|
||||
*
|
||||
* We handle them differently as we only allow a single multiparted
|
||||
* processing message
|
||||
*/
|
||||
|
||||
if (session->mp->header.sequnum == ntohs(header->sequnum) &&
|
||||
session->mp->header.timestamp == ntohl(header->timestamp)) {
|
||||
/* First case */
|
||||
|
||||
/* Make sure we have enough allocated memory */
|
||||
if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
|
||||
session->mp->header.tlen <= ntohs(header->cpart)) {
|
||||
/* There happened to be some corruption on the stream;
|
||||
* continue wihtout this part
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
|
||||
length - sizeof(struct RTPHeader));
|
||||
|
||||
session->mp->len += length - sizeof(struct RTPHeader);
|
||||
|
||||
bwc_add_recv(session->bwc, length);
|
||||
|
||||
if (session->mp->len == session->mp->header.tlen) {
|
||||
/* Received a full message; now push it for the further
|
||||
* processing.
|
||||
*/
|
||||
if (session->mcb)
|
||||
session->mcb (session->cs, session->mp);
|
||||
else
|
||||
free(session->mp);
|
||||
|
||||
session->mp = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Second case */
|
||||
|
||||
if (session->mp->header.timestamp > ntohl(header->timestamp))
|
||||
/* The received message part is from the old message;
|
||||
* discard it.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
/* Measure missing parts of the old message */
|
||||
bwc_add_lost(session->bwc,
|
||||
(session->mp->header.tlen - session->mp->len) +
|
||||
|
||||
/* Must account sizes of rtp headers too */
|
||||
((session->mp->header.tlen - session->mp->len) /
|
||||
MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
|
||||
|
||||
/* Push the previous message for processing */
|
||||
if (session->mcb)
|
||||
session->mcb (session->cs, session->mp);
|
||||
else
|
||||
free(session->mp);
|
||||
|
||||
session->mp = NULL;
|
||||
goto NEW_MULTIPARTED;
|
||||
}
|
||||
} else {
|
||||
/* In this case threat the message as if it was received in order
|
||||
*/
|
||||
|
||||
/* This is also a point for new multiparted messages */
|
||||
NEW_MULTIPARTED:
|
||||
|
||||
/* Only allow messages which have arrived in order;
|
||||
* drop late messages
|
||||
*/
|
||||
if (chloss(session, header)) {
|
||||
return 0;
|
||||
} else {
|
||||
/* Message is not late; pick up the latest parameters */
|
||||
session->rsequnum = ntohs(header->sequnum);
|
||||
session->rtimestamp = ntohl(header->timestamp);
|
||||
}
|
||||
|
||||
bwc_add_recv(session->bwc, length);
|
||||
|
||||
/* Again, only store message if handler is present
|
||||
*/
|
||||
if (session->mcb) {
|
||||
session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
|
||||
|
||||
/* Reposition data if necessary */
|
||||
if (ntohs(header->cpart));
|
||||
|
||||
memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free ( msg->header );
|
||||
free ( msg );
|
||||
}
|
||||
|
||||
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
|
||||
{
|
||||
RTPSession *retu = calloc(1, sizeof(RTPSession));
|
||||
|
||||
if ( !retu ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( -1 == m_callback_rtp_packet(messenger, friend_num, payload_type, rtp_handle_packet, retu)) {
|
||||
LOGGER_ERROR("Error setting custom register handler for rtp session");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num);
|
||||
|
||||
retu->version = RTP_VERSION; /* It's always 2 */
|
||||
retu->padding = 0; /* If some additional data is needed about the packet */
|
||||
retu->extension = 0; /* If extension to header is needed */
|
||||
retu->cc = 1; /* Amount of contributors */
|
||||
retu->csrc = NULL; /* Container */
|
||||
retu->ssrc = random_int();
|
||||
retu->marker = 0;
|
||||
retu->payload_type = payload_type % 128;
|
||||
|
||||
retu->dest = friend_num;
|
||||
|
||||
retu->rsequnum = retu->sequnum = 0;
|
||||
|
||||
retu->ext_header = NULL; /* When needed allocate */
|
||||
|
||||
|
||||
if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) {
|
||||
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
|
||||
|
||||
/* Also set payload type as prefix */
|
||||
retu->prefix = payload_type;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
return retu;
|
||||
}
|
||||
|
||||
void rtp_kill ( RTPSession *session, Messenger *messenger )
|
||||
{
|
||||
if ( !session ) return;
|
||||
|
||||
m_callback_rtp_packet(messenger, session->dest, session->prefix, NULL, NULL);
|
||||
|
||||
free ( session->ext_header );
|
||||
free ( session->csrc );
|
||||
|
||||
LOGGER_DEBUG("Terminated RTP session: %p", session);
|
||||
|
||||
/* And finally free session */
|
||||
free ( session );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
156
toxav/rtp.h
156
toxav/rtp.h
|
@ -1,6 +1,6 @@
|
|||
/** rtp.h
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
|
@ -19,109 +19,91 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __TOXRTP
|
||||
#define __TOXRTP
|
||||
|
||||
#define RTP_VERSION 2
|
||||
#include <inttypes.h>
|
||||
// #include <pthread.h>
|
||||
#ifndef RTP_H
|
||||
#define RTP_H
|
||||
|
||||
#include "bwcontroler.h"
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
#define MAX_SEQU_NUM 65535
|
||||
#define MAX_RTP_SIZE 65535
|
||||
|
||||
typedef enum {
|
||||
rtp_ErrorSending = -40
|
||||
} RTPError;
|
||||
/**
|
||||
* Standard rtp header
|
||||
*/
|
||||
typedef struct _RTPHeader {
|
||||
uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
|
||||
uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
|
||||
uint16_t sequnum; /* Sequence Number */
|
||||
uint32_t timestamp; /* Timestamp */
|
||||
uint32_t ssrc; /* SSRC */
|
||||
uint32_t csrc[16]; /* CSRC's table */
|
||||
uint32_t length; /* Length of the header in payload string. */
|
||||
|
||||
} RTPHeader;
|
||||
#include "stdbool.h"
|
||||
|
||||
/**
|
||||
* Standard rtp extension header.
|
||||
* Payload type identifier. Also used as rtp callback prefix.
|
||||
*/
|
||||
typedef struct _RTPExtHeader {
|
||||
uint16_t type; /* Extension profile */
|
||||
uint16_t length; /* Number of extensions */
|
||||
uint32_t *table; /* Extension's table */
|
||||
enum {
|
||||
rtp_TypeAudio = 192,
|
||||
rtp_TypeVideo,
|
||||
};
|
||||
|
||||
} RTPExtHeader;
|
||||
struct RTPHeader {
|
||||
/* Standard RTP header */
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
uint16_t cc: 4; /* Contributing sources count */
|
||||
uint16_t xe: 1; /* Extra header */
|
||||
uint16_t pe: 1; /* Padding */
|
||||
uint16_t ve: 2; /* Version */
|
||||
|
||||
/**
|
||||
* Standard rtp message.
|
||||
*/
|
||||
typedef struct _RTPMessage {
|
||||
RTPHeader *header;
|
||||
RTPExtHeader *ext_header;
|
||||
uint16_t pt: 7; /* Payload type */
|
||||
uint16_t ma: 1; /* Marker */
|
||||
#else
|
||||
uint16_t ve: 2; /* Version */
|
||||
uint16_t pe: 1; /* Padding */
|
||||
uint16_t xe: 1; /* Extra header */
|
||||
uint16_t cc: 4; /* Contributing sources count */
|
||||
|
||||
uint8_t data[MAX_RTP_SIZE];
|
||||
uint32_t length;
|
||||
uint16_t ma: 1; /* Marker */
|
||||
uint16_t pt: 7; /* Payload type */
|
||||
#endif
|
||||
|
||||
struct _RTPMessage *next;
|
||||
} RTPMessage;
|
||||
uint16_t sequnum;
|
||||
uint32_t timestamp;
|
||||
uint32_t ssrc;
|
||||
uint32_t csrc[16];
|
||||
|
||||
/* Non-standard TOX-specific fields */
|
||||
uint16_t cpart;/* Data offset of the current part */
|
||||
uint16_t tlen; /* Total message lenght */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Check alignment */
|
||||
typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ];
|
||||
|
||||
struct RTPMessage {
|
||||
uint16_t len;
|
||||
|
||||
struct RTPHeader header;
|
||||
uint8_t data[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Check alignment */
|
||||
typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ];
|
||||
|
||||
/**
|
||||
* RTP control session.
|
||||
*/
|
||||
typedef struct _RTPSession {
|
||||
uint8_t version;
|
||||
uint8_t padding;
|
||||
uint8_t extension;
|
||||
uint8_t cc;
|
||||
uint8_t marker;
|
||||
uint8_t payload_type;
|
||||
uint16_t sequnum; /* Set when sending */
|
||||
uint16_t rsequnum; /* Check when recving msg */
|
||||
uint32_t timestamp;
|
||||
uint32_t ssrc;
|
||||
uint32_t *csrc;
|
||||
typedef struct {
|
||||
uint8_t payload_type;
|
||||
uint16_t sequnum; /* Sending sequence number */
|
||||
uint16_t rsequnum; /* Receiving sequence number */
|
||||
uint32_t rtimestamp;
|
||||
uint32_t ssrc;
|
||||
|
||||
/* If some additional data must be sent via message
|
||||
* apply it here. Only by allocating this member you will be
|
||||
* automatically placing it within a message.
|
||||
*/
|
||||
RTPExtHeader *ext_header;
|
||||
struct RTPMessage *mp; /* Expected parted message */
|
||||
|
||||
/* Msg prefix for core to know when recving */
|
||||
uint8_t prefix;
|
||||
|
||||
int dest;
|
||||
|
||||
struct _CSSession *cs;
|
||||
Messenger *m;
|
||||
uint32_t friend_number;
|
||||
|
||||
BWControler *bwc;
|
||||
void *cs;
|
||||
int (*mcb) (void *, struct RTPMessage *msg);
|
||||
} RTPSession;
|
||||
|
||||
/**
|
||||
* Must be called before calling any other rtp function.
|
||||
*/
|
||||
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num );
|
||||
|
||||
/**
|
||||
* Terminate the session.
|
||||
*/
|
||||
void rtp_kill ( RTPSession *session, Messenger *messenger );
|
||||
RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num,
|
||||
BWControler *bwc, void *cs,
|
||||
int (*mcb) (void *, struct RTPMessage *));
|
||||
void rtp_kill (RTPSession *session);
|
||||
int rtp_allow_receiving (RTPSession *session);
|
||||
int rtp_stop_receiving (RTPSession *session);
|
||||
int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length);
|
||||
|
||||
/**
|
||||
* Sends msg to _RTPSession::dest
|
||||
*/
|
||||
int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
|
||||
|
||||
/**
|
||||
* Dealloc msg.
|
||||
*/
|
||||
void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
|
||||
|
||||
|
||||
|
||||
#endif /* __TOXRTP */
|
||||
#endif /* RTP_H */
|
||||
|
|
1727
toxav/toxav.c
1727
toxav/toxav.c
File diff suppressed because it is too large
Load Diff
768
toxav/toxav.h
768
toxav/toxav.h
|
@ -1,285 +1,698 @@
|
|||
/** toxav.h
|
||||
/* toxav.h
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOXAV_H
|
||||
#define TOXAV_H
|
||||
|
||||
#ifndef __TOXAV
|
||||
#define __TOXAV
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _ToxAv ToxAv;
|
||||
|
||||
/* vpx_image_t */
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
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);
|
||||
|
||||
/** \page av Public audio/video API for Tox clients.
|
||||
*
|
||||
* This API can handle multiple calls. Each call has its state, in very rare
|
||||
* occasions the library can change the state of the call without apps knowledge.
|
||||
*
|
||||
*/
|
||||
/** \subsection events Events and callbacks
|
||||
*
|
||||
* As in Core API, events are handled by callbacks. One callback can be
|
||||
* registered per event. All events have a callback function type named
|
||||
* `toxav_{event}_cb` and a function to register it named `toxav_callback_{event}`.
|
||||
* Passing a NULL callback will result in no callback being registered for that
|
||||
* event. Only one callback per event can be registered, so if a client needs
|
||||
* multiple event listeners, it needs to implement the dispatch functionality
|
||||
* itself. Unlike Core API, lack of some event handlers will cause the the
|
||||
* library to drop calls before they are started. Hanging up call from a
|
||||
* callback causes undefined behaviour.
|
||||
*
|
||||
*/
|
||||
/** \subsection threading Threading implications
|
||||
*
|
||||
* Unlike the Core API, this API is fully thread-safe. The library will ensure
|
||||
* the proper synchronization of parallel calls.
|
||||
*
|
||||
* A common way to run ToxAV (multiple or single instance) is to have a thread,
|
||||
* separate from tox instance thread, running a simple toxav_iterate loop,
|
||||
* sleeping for toxav_iteration_interval * milliseconds on each iteration.
|
||||
*
|
||||
* An important thing to note is that events are triggered from both tox and
|
||||
* toxav thread (see above). Audio and video receive frame events are triggered
|
||||
* from toxav thread while all the other events are triggered from tox thread.
|
||||
*
|
||||
* Tox thread has priority with mutex mechanisms. Any api function can
|
||||
* fail if mutexes are held by tox thread in which case they will set SYNC
|
||||
* error code.
|
||||
*/
|
||||
/**
|
||||
* External Tox type.
|
||||
*/
|
||||
#ifndef TOX_DEFINED
|
||||
#define TOX_DEFINED
|
||||
typedef struct Tox Tox;
|
||||
#endif
|
||||
|
||||
#define RTP_PAYLOAD_SIZE 65535
|
||||
|
||||
#endif /* TOX_DEFINED */
|
||||
|
||||
/**
|
||||
* Callbacks ids that handle the call states.
|
||||
* ToxAV.
|
||||
*/
|
||||
typedef enum {
|
||||
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;
|
||||
/**
|
||||
* The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
|
||||
* instance, and Tox instance can have only one ToxAV instance. One must make
|
||||
* sure to close ToxAV instance prior closing Tox instance otherwise undefined
|
||||
* behaviour occurs. Upon closing of ToxAV instance, all active calls will be
|
||||
* forcibly terminated without notifying peers.
|
||||
*
|
||||
*/
|
||||
#ifndef TOXAV_DEFINED
|
||||
#define TOXAV_DEFINED
|
||||
typedef struct ToxAV ToxAV;
|
||||
#endif /* TOXAV_DEFINED */
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: API version
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* The major version number. Incremented when the API or ABI changes in an
|
||||
* incompatible way.
|
||||
*/
|
||||
#define TOXAV_VERSION_MAJOR 0u
|
||||
|
||||
/**
|
||||
* Call type identifier.
|
||||
* The minor version number. Incremented when functionality is added without
|
||||
* breaking the API or ABI. Set to 0 when the major version number is
|
||||
* incremented.
|
||||
*/
|
||||
typedef enum {
|
||||
av_TypeAudio = 192,
|
||||
av_TypeVideo
|
||||
} ToxAvCallType;
|
||||
|
||||
|
||||
typedef enum {
|
||||
av_CallNonExistent = -1,
|
||||
av_CallInviting, /* when sending call invite */
|
||||
av_CallStarting, /* when getting call invite */
|
||||
av_CallActive,
|
||||
av_CallHold,
|
||||
av_CallHungUp
|
||||
} ToxAvCallState;
|
||||
#define TOXAV_VERSION_MINOR 0u
|
||||
|
||||
/**
|
||||
* Error indicators. Values under -20 are reserved for toxcore.
|
||||
* The patch or revision number. Incremented when bugfixes are applied without
|
||||
* changing any functionality or API or ABI.
|
||||
*/
|
||||
typedef enum {
|
||||
av_ErrorNone = 0,
|
||||
av_ErrorUnknown = -1, /* Unknown error */
|
||||
av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
|
||||
av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
|
||||
av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
|
||||
av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
|
||||
av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */
|
||||
av_ErrorSettingVideoResolution = -31, /* Error setting resolution */
|
||||
av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */
|
||||
av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */
|
||||
av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */
|
||||
av_ErrorEncodingAudio = -35, /* opus_encode failed */
|
||||
av_ErrorSendingPayload = -40, /* Sending lossy packet failed */
|
||||
av_ErrorCreatingRtpSessions = -41, /* One of the rtp sessions failed to initialize */
|
||||
av_ErrorNoRtpSession = -50, /* Trying to perform rtp action on invalid session */
|
||||
av_ErrorInvalidCodecState = -51, /* Codec state not initialized */
|
||||
av_ErrorPacketTooLarge = -52, /* Split packet exceeds it's limit */
|
||||
} ToxAvError;
|
||||
|
||||
#define TOXAV_VERSION_PATCH 0u
|
||||
|
||||
/**
|
||||
* Locally supported capabilities.
|
||||
* A macro to check at preprocessing time whether the client code is compatible
|
||||
* with the installed version of ToxAV.
|
||||
*/
|
||||
typedef enum {
|
||||
av_AudioEncoding = 1 << 0,
|
||||
av_AudioDecoding = 1 << 1,
|
||||
av_VideoEncoding = 1 << 2,
|
||||
av_VideoDecoding = 1 << 3
|
||||
} ToxAvCapabilities;
|
||||
|
||||
#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
|
||||
(TOXAV_VERSION_MAJOR == MAJOR && \
|
||||
(TOXAV_VERSION_MINOR > MINOR || \
|
||||
(TOXAV_VERSION_MINOR == MINOR && \
|
||||
TOXAV_VERSION_PATCH >= PATCH)))
|
||||
|
||||
/**
|
||||
* Encoding settings.
|
||||
* A macro to make compilation fail if the client code is not compatible with
|
||||
* the installed version of ToxAV.
|
||||
*/
|
||||
typedef struct _ToxAvCSettings {
|
||||
ToxAvCallType call_type;
|
||||
|
||||
uint32_t video_bitrate; /* In kbits/s */
|
||||
uint16_t max_video_width; /* In px */
|
||||
uint16_t max_video_height; /* In px */
|
||||
|
||||
uint32_t audio_bitrate; /* In bits/s */
|
||||
uint16_t audio_frame_duration; /* In ms */
|
||||
uint32_t audio_sample_rate; /* In Hz */
|
||||
uint32_t audio_channels;
|
||||
} ToxAvCSettings;
|
||||
|
||||
extern const ToxAvCSettings av_DefaultSettings;
|
||||
#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
|
||||
typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
|
||||
|
||||
/**
|
||||
* Start new A/V session. There can only be one session at the time.
|
||||
* A convenience macro to call toxav_version_is_compatible with the currently
|
||||
* compiling API version.
|
||||
*/
|
||||
ToxAv *toxav_new(Tox *messenger, int32_t max_calls);
|
||||
#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
|
||||
toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
|
||||
|
||||
/**
|
||||
* Remove A/V session.
|
||||
* Return the major version number of the library. Can be used to display the
|
||||
* ToxAV library version or to check whether the client is compatible with the
|
||||
* dynamically linked version of ToxAV.
|
||||
*/
|
||||
void toxav_kill(ToxAv *av);
|
||||
uint32_t toxav_version_major(void);
|
||||
|
||||
/**
|
||||
* Returns the interval in milliseconds when the next toxav_do() should be called.
|
||||
* If no call is active at the moment returns 200.
|
||||
* Return the minor version number of the library.
|
||||
*/
|
||||
uint32_t toxav_do_interval(ToxAv *av);
|
||||
uint32_t toxav_version_minor(void);
|
||||
|
||||
/**
|
||||
* Main loop for the session. Best called right after tox_do();
|
||||
* Return the patch number of the library.
|
||||
*/
|
||||
void toxav_do(ToxAv *av);
|
||||
uint32_t toxav_version_patch(void);
|
||||
|
||||
/**
|
||||
* Register callback for call state.
|
||||
* Return whether the compiled library version is compatible with the passed
|
||||
* version numbers.
|
||||
*/
|
||||
void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata);
|
||||
bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Creation and destruction
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_ERR_NEW {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_NEW_OK,
|
||||
/**
|
||||
* One of the arguments to the function was NULL when it was not expected.
|
||||
*/
|
||||
TOXAV_ERR_NEW_NULL,
|
||||
/**
|
||||
* Memory allocation failure while trying to allocate structures required for
|
||||
* the A/V session.
|
||||
*/
|
||||
TOXAV_ERR_NEW_MALLOC,
|
||||
/**
|
||||
* Attempted to create a second session for the same Tox instance.
|
||||
*/
|
||||
TOXAV_ERR_NEW_MULTIPLE,
|
||||
} TOXAV_ERR_NEW;
|
||||
|
||||
/**
|
||||
* Register callback for audio data.
|
||||
* Start new A/V session. There can only be only one session per Tox instance.
|
||||
*/
|
||||
void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata);
|
||||
ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
|
||||
|
||||
/**
|
||||
* Register callback for video data.
|
||||
* Releases all resources associated with the A/V session.
|
||||
*
|
||||
* If any calls were ongoing, these will be forcibly terminated without
|
||||
* notifying peers. After calling this function, no other functions may be
|
||||
* called and the av pointer becomes invalid.
|
||||
*/
|
||||
void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata);
|
||||
void toxav_kill(ToxAV *toxAV);
|
||||
|
||||
/**
|
||||
* Call user. Use its friend_id.
|
||||
* Returns the Tox instance the A/V object was created for.
|
||||
*/
|
||||
int toxav_call(ToxAv *av,
|
||||
int32_t *call_index,
|
||||
int friend_id,
|
||||
const ToxAvCSettings *csettings,
|
||||
int ringing_seconds);
|
||||
Tox *toxav_get_tox(const ToxAV *toxAV);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V event loop
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* Returns the interval in milliseconds when the next toxav_iterate call should
|
||||
* be. If no call is active at the moment, this function returns 200.
|
||||
*/
|
||||
uint32_t toxav_iteration_interval(const ToxAV *toxAV);
|
||||
|
||||
/**
|
||||
* Hangup active call.
|
||||
* Main loop for the session. This function needs to be called in intervals of
|
||||
* toxav_iteration_interval() milliseconds. It is best called in the separate
|
||||
* thread from tox_iterate.
|
||||
*/
|
||||
int toxav_hangup(ToxAv *av, int32_t call_index);
|
||||
void toxav_iterate(ToxAV *toxAV);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call setup
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_ERR_CALL {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_CALL_OK,
|
||||
/**
|
||||
* A resource allocation error occurred while trying to create the structures
|
||||
* required for the call.
|
||||
*/
|
||||
TOXAV_ERR_CALL_MALLOC,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
TOXAV_ERR_CALL_SYNC,
|
||||
/**
|
||||
* The friend number did not designate a valid friend.
|
||||
*/
|
||||
TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* The friend was valid, but not currently connected.
|
||||
*/
|
||||
TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED,
|
||||
/**
|
||||
* Attempted to call a friend while already in an audio or video call with
|
||||
* them.
|
||||
*/
|
||||
TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL,
|
||||
/**
|
||||
* Audio or video bit rate is invalid.
|
||||
*/
|
||||
TOXAV_ERR_CALL_INVALID_BIT_RATE,
|
||||
} TOXAV_ERR_CALL;
|
||||
|
||||
/**
|
||||
* Answer incoming call. Pass the csettings that you will use.
|
||||
* Call a friend. This will start ringing the friend.
|
||||
*
|
||||
* It is the client's responsibility to stop ringing after a certain timeout,
|
||||
* if such behaviour is desired. If the client does not stop ringing, the
|
||||
* library will not stop until the friend is disconnected. Audio and video
|
||||
* receiving are both enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that should be called.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings );
|
||||
bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||
uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
|
||||
|
||||
/**
|
||||
* Reject incoming call.
|
||||
* The function type for the call callback.
|
||||
*
|
||||
* @param friend_number The friend number from which the call is incoming.
|
||||
* @param audio_enabled True if friend is sending audio.
|
||||
* @param video_enabled True if friend is sending video.
|
||||
*/
|
||||
int toxav_reject(ToxAv *av, int32_t call_index, const char *reason);
|
||||
typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled,
|
||||
bool video_enabled, void *user_data);
|
||||
|
||||
/**
|
||||
* Cancel outgoing request.
|
||||
* Set the callback for the `call` event. Pass NULL to unset.
|
||||
*
|
||||
*/
|
||||
int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason);
|
||||
void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
|
||||
|
||||
typedef enum TOXAV_ERR_ANSWER {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_OK,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_SYNC,
|
||||
/**
|
||||
* Failed to initialize codecs for call session. Note that codec initiation
|
||||
* will fail if there is no receive callback registered for either audio or
|
||||
* video.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_CODEC_INITIALIZATION,
|
||||
/**
|
||||
* The friend number did not designate a valid friend.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* The friend was valid, but they are not currently trying to initiate a call.
|
||||
* This is also returned if this client is already in a call with the friend.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING,
|
||||
/**
|
||||
* Audio or video bit rate is invalid.
|
||||
*/
|
||||
TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
|
||||
} TOXAV_ERR_ANSWER;
|
||||
|
||||
/**
|
||||
* Notify peer that we are changing codec settings.
|
||||
* Accept an incoming call.
|
||||
*
|
||||
* If answering fails for any reason, the call will still be pending and it is
|
||||
* possible to try and answer it later. Audio and video receiving are both
|
||||
* enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that is calling.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings);
|
||||
bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
|
||||
TOXAV_ERR_ANSWER *error);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call state graph
|
||||
*
|
||||
******************************************************************************/
|
||||
enum TOXAV_FRIEND_CALL_STATE {
|
||||
/**
|
||||
* Set by the AV core if an error occurred on the remote end or if friend
|
||||
* timed out. This is the final state after which no more state
|
||||
* transitions can occur for the call. This call state will never be triggered
|
||||
* in combination with other call states.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_ERROR = 1,
|
||||
/**
|
||||
* The call has finished. This is the final state after which no more state
|
||||
* transitions can occur for the call. This call state will never be
|
||||
* triggered in combination with other call states.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_FINISHED = 2,
|
||||
/**
|
||||
* The flag that marks that friend is sending audio.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_SENDING_A = 4,
|
||||
/**
|
||||
* The flag that marks that friend is sending video.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_SENDING_V = 8,
|
||||
/**
|
||||
* The flag that marks that friend is receiving audio.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16,
|
||||
/**
|
||||
* The flag that marks that friend is receiving video.
|
||||
*/
|
||||
TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminate transmission. Note that transmission will be
|
||||
* terminated without informing remote peer. Usually called when we can't inform peer.
|
||||
* The function type for the call_state callback.
|
||||
*
|
||||
* @param friend_number The friend number for which the call state changed.
|
||||
* @param state The bitmask of the new call state which is guaranteed to be
|
||||
* different than the previous state. The state is set to 0 when the call is
|
||||
* paused. The bitmask represents all the activities currently performed by the
|
||||
* friend.
|
||||
*/
|
||||
int toxav_stop_call(ToxAv *av, int32_t call_index);
|
||||
typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
|
||||
|
||||
/**
|
||||
* Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*.
|
||||
* Also, it must be called when call is started
|
||||
* Set the callback for the `call_state` event. Pass NULL to unset.
|
||||
*
|
||||
*/
|
||||
int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video);
|
||||
void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Call control
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_CALL_CONTROL {
|
||||
/**
|
||||
* Resume a previously paused call. Only valid if the pause was caused by this
|
||||
* client, if not, this control is ignored. Not valid before the call is accepted.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_RESUME,
|
||||
/**
|
||||
* Put a call on hold. Not valid before the call is accepted.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_PAUSE,
|
||||
/**
|
||||
* Reject a call if it was not answered, yet. Cancel a call after it was
|
||||
* answered.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_CANCEL,
|
||||
/**
|
||||
* Request that the friend stops sending audio. Regardless of the friend's
|
||||
* compliance, this will cause the audio_receive_frame event to stop being
|
||||
* triggered on receiving an audio frame from the friend.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_MUTE_AUDIO,
|
||||
/**
|
||||
* Calling this control will notify client to start sending audio again.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
|
||||
/**
|
||||
* Request that the friend stops sending video. Regardless of the friend's
|
||||
* compliance, this will cause the video_receive_frame event to stop being
|
||||
* triggered on receiving a video frame from the friend.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_HIDE_VIDEO,
|
||||
/**
|
||||
* Calling this control will notify client to start sending video again.
|
||||
*/
|
||||
TOXAV_CALL_CONTROL_SHOW_VIDEO,
|
||||
} TOXAV_CALL_CONTROL;
|
||||
|
||||
typedef enum TOXAV_ERR_CALL_CONTROL {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_CALL_CONTROL_OK,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
TOXAV_ERR_CALL_CONTROL_SYNC,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend. Before the call is
|
||||
* answered, only CANCEL is a valid control.
|
||||
*/
|
||||
TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL,
|
||||
/**
|
||||
* Happens if user tried to pause an already paused call or if trying to
|
||||
* resume a call that is not paused.
|
||||
*/
|
||||
TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
|
||||
} TOXAV_ERR_CALL_CONTROL;
|
||||
|
||||
/**
|
||||
* Clears transmission data. Call this at the end of the transmission.
|
||||
* Sends a call control command to a friend.
|
||||
*
|
||||
* @param friend_number The friend number of the friend this client is in a call
|
||||
* with.
|
||||
* @param control The control command to send.
|
||||
*
|
||||
* @return true on success.
|
||||
*/
|
||||
int toxav_kill_transmission(ToxAv *av, int32_t call_index);
|
||||
bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control,
|
||||
TOXAV_ERR_CALL_CONTROL *error);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: Controlling bit rates
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_ERR_BIT_RATE_SET {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_OK,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_SYNC,
|
||||
/**
|
||||
* The audio bit rate passed was not one of the supported values.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
|
||||
/**
|
||||
* The video bit rate passed was not one of the supported values.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend.
|
||||
*/
|
||||
TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
|
||||
} TOXAV_ERR_BIT_RATE_SET;
|
||||
|
||||
/**
|
||||
* Encode video frame.
|
||||
* Set the bit rate to be used in subsequent audio/video frames.
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
|
||||
* audio sending. Set to -1 to leave unchanged.
|
||||
* @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
|
||||
* video sending. Set to -1 to leave unchanged.
|
||||
*
|
||||
*/
|
||||
int toxav_prepare_video_frame ( ToxAv *av,
|
||||
int32_t call_index,
|
||||
uint8_t *dest,
|
||||
int dest_max,
|
||||
vpx_image_t *input);
|
||||
bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
|
||||
int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
|
||||
|
||||
/**
|
||||
* Send encoded video packet.
|
||||
* The function type for the bit_rate_status callback. The event is triggered
|
||||
* when the network becomes too saturated for current bit rates at which
|
||||
* point core suggests new bit rates.
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
|
||||
* @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
|
||||
*/
|
||||
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size);
|
||||
typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||
uint32_t video_bit_rate, void *user_data);
|
||||
|
||||
/**
|
||||
* Encode audio frame.
|
||||
* Set the callback for the `bit_rate_status` event. Pass NULL to unset.
|
||||
*
|
||||
*/
|
||||
int toxav_prepare_audio_frame ( ToxAv *av,
|
||||
int32_t call_index,
|
||||
uint8_t *dest,
|
||||
int dest_max,
|
||||
const int16_t *frame,
|
||||
int frame_size);
|
||||
void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V sending
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_ERR_SEND_FRAME {
|
||||
/**
|
||||
* The function returned successfully.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_OK,
|
||||
/**
|
||||
* In case of video, one of Y, U, or V was NULL. In case of audio, the samples
|
||||
* data pointer was NULL.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_NULL,
|
||||
/**
|
||||
* The friend_number passed did not designate a valid friend.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND,
|
||||
/**
|
||||
* This client is currently not in a call with the friend.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL,
|
||||
/**
|
||||
* Synchronization error occurred.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_SYNC,
|
||||
/**
|
||||
* One of the frame parameters was invalid. E.g. the resolution may be too
|
||||
* small or too large, or the audio sampling rate may be unsupported.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_INVALID,
|
||||
/**
|
||||
* Either friend turned off audio or video receiving or we turned off sending
|
||||
* for the said payload.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED,
|
||||
/**
|
||||
* Failed to push frame through rtp interface.
|
||||
*/
|
||||
TOXAV_ERR_SEND_FRAME_RTP_FAILED,
|
||||
} TOXAV_ERR_SEND_FRAME;
|
||||
|
||||
/**
|
||||
* Send encoded audio frame.
|
||||
* Send an audio frame to a friend.
|
||||
*
|
||||
* The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
|
||||
* Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
|
||||
* For mono audio, this has no meaning, every sample is subsequent. For stereo,
|
||||
* this means the expected format is LRLRLR... with samples for left and right
|
||||
* alternating.
|
||||
*
|
||||
* @param friend_number The friend number of the friend to which to send an
|
||||
* audio frame.
|
||||
* @param pcm An array of audio samples. The size of this array must be
|
||||
* sample_count * channels.
|
||||
* @param sample_count Number of samples in this frame. Valid numbers here are
|
||||
* ((sample rate) * (audio length) / 1000), where audio length can be
|
||||
* 2.5, 5, 10, 20, 40 or 60 millseconds.
|
||||
* @param channels Number of audio channels. Supported values are 1 and 2.
|
||||
* @param sampling_rate Audio sampling rate used in this frame. Valid sampling
|
||||
* rates are 8000, 12000, 16000, 24000, or 48000.
|
||||
*/
|
||||
int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size);
|
||||
bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
|
||||
size_t sample_count, uint8_t channels, uint32_t sampling_rate,
|
||||
TOXAV_ERR_SEND_FRAME *error);
|
||||
|
||||
/**
|
||||
* Get codec settings from the peer. These were exchanged during call initialization
|
||||
* or when peer send us new csettings.
|
||||
* Send a video frame to a friend.
|
||||
*
|
||||
* Y - plane should be of size: height * width
|
||||
* U - plane should be of size: (height/2) * (width/2)
|
||||
* V - plane should be of size: (height/2) * (width/2)
|
||||
*
|
||||
* @param friend_number The friend number of the friend to which to send a video
|
||||
* frame.
|
||||
* @param width Width of the frame in pixels.
|
||||
* @param height Height of the frame in pixels.
|
||||
* @param y Y (Luminance) plane data.
|
||||
* @param u U (Chroma) plane data.
|
||||
* @param v V (Chroma) plane data.
|
||||
*/
|
||||
int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest );
|
||||
bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
|
||||
uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
|
||||
TOXAV_ERR_SEND_FRAME *error);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* :: A/V receiving
|
||||
*
|
||||
******************************************************************************/
|
||||
/**
|
||||
* The function type for the audio_receive_frame callback. The callback can be
|
||||
* called multiple times per single iteration depending on the amount of queued
|
||||
* frames in the buffer. The received format is the same as in send function.
|
||||
*
|
||||
* @param friend_number The friend number of the friend who sent an audio frame.
|
||||
* @param pcm An array of audio samples (sample_count * channels elements).
|
||||
* @param sample_count The number of audio samples per channel in the PCM array.
|
||||
* @param channels Number of audio channels.
|
||||
* @param sampling_rate Sampling rate used in this frame.
|
||||
*
|
||||
*/
|
||||
typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
|
||||
size_t sample_count, uint8_t channels, uint32_t sampling_rate,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* Get friend id of peer participating in conversation.
|
||||
* Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
|
||||
*
|
||||
*/
|
||||
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer );
|
||||
void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
|
||||
|
||||
/**
|
||||
* Get current call state.
|
||||
* The function type for the video_receive_frame callback.
|
||||
*
|
||||
* @param friend_number The friend number of the friend who sent a video frame.
|
||||
* @param width Width of the frame in pixels.
|
||||
* @param height Height of the frame in pixels.
|
||||
* @param y
|
||||
* @param u
|
||||
* @param v Plane data.
|
||||
* The size of plane data is derived from width and height where
|
||||
* Y = MAX(width, abs(ystride)) * height,
|
||||
* U = MAX(width/2, abs(ustride)) * (height/2) and
|
||||
* V = MAX(width/2, abs(vstride)) * (height/2).
|
||||
* @param ystride
|
||||
* @param ustride
|
||||
* @param vstride Strides data. Strides represent padding for each plane
|
||||
* that may or may not be present. You must handle strides in
|
||||
* your image processing code. Strides are negative if the
|
||||
* image is bottom-up hence why you MUST abs() it when
|
||||
* calculating plane buffer size.
|
||||
*/
|
||||
ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index );
|
||||
typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
|
||||
uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
|
||||
int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
|
||||
|
||||
/**
|
||||
* Is certain capability supported. Used to determine if encoding/decoding is ready.
|
||||
* Set the callback for the `video_receive_frame` event. Pass NULL to unset.
|
||||
*
|
||||
*/
|
||||
int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability );
|
||||
void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
|
||||
|
||||
/**
|
||||
* Returns tox reference.
|
||||
* NOTE Compatibility with old toxav group calls TODO remove
|
||||
*/
|
||||
Tox *toxav_get_tox (ToxAv *av);
|
||||
|
||||
/**
|
||||
* Returns number of active calls or -1 on error.
|
||||
*/
|
||||
int toxav_get_active_count (ToxAv *av);
|
||||
|
||||
/* Create a new toxav group.
|
||||
*
|
||||
* return group number on success.
|
||||
|
@ -290,7 +703,7 @@ int toxav_get_active_count (ToxAv *av);
|
|||
*
|
||||
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
|
||||
*/
|
||||
int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t,
|
||||
int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t,
|
||||
unsigned int, void *), void *userdata);
|
||||
|
||||
/* Join a AV group (you need to have been invited first.)
|
||||
|
@ -304,7 +717,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, con
|
|||
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
|
||||
*/
|
||||
int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
|
||||
void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
|
||||
void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
|
||||
|
||||
/* Send audio to the group chat.
|
||||
*
|
||||
|
@ -325,5 +738,4 @@ int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsign
|
|||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TOXAV */
|
||||
#endif /* TOXAV_H */
|
||||
|
|
81
toxav/toxav_old.c
Normal file
81
toxav/toxav_old.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* toxav_old.h
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* This file contains the group chats code for the backwards compatibility.
|
||||
*/
|
||||
|
||||
#include "toxav.h"
|
||||
#include "group.h"
|
||||
|
||||
/* Create a new toxav group.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*
|
||||
* Audio data callback format:
|
||||
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
|
||||
*
|
||||
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
|
||||
*/
|
||||
int toxav_add_av_groupchat(struct Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int,
|
||||
uint8_t, unsigned int, void *), void *userdata)
|
||||
{
|
||||
Messenger *m = (Messenger *)tox;
|
||||
return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
|
||||
}
|
||||
|
||||
/* Join a AV group (you need to have been invited first.)
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*
|
||||
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
|
||||
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
|
||||
*
|
||||
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
|
||||
*/
|
||||
int toxav_join_av_groupchat(struct Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
|
||||
void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = (Messenger *)tox;
|
||||
return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata);
|
||||
}
|
||||
|
||||
/* Send audio to the group chat.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*
|
||||
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
|
||||
*
|
||||
* Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
|
||||
* Valid number of channels are 1 or 2.
|
||||
* Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
|
||||
*
|
||||
* Recommended values are: samples = 960, channels = 1, sample_rate = 48000
|
||||
*/
|
||||
int toxav_group_send_audio(struct Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
|
||||
unsigned int sample_rate)
|
||||
{
|
||||
Messenger *m = (Messenger *)tox;
|
||||
return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate);
|
||||
}
|
264
toxav/video.c
Normal file
264
toxav/video.c
Normal file
|
@ -0,0 +1,264 @@
|
|||
/** video.c
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "video.h"
|
||||
#include "msi.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/network.h"
|
||||
|
||||
#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
|
||||
#define VIDEO_DECODE_BUFFER_SIZE 20
|
||||
|
||||
VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
|
||||
{
|
||||
VCSession *vc = calloc(sizeof(VCSession), 1);
|
||||
|
||||
if (!vc) {
|
||||
LOGGER_WARNING("Allocation failed! Application might misbehave!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(vc->queue_mutex) != 0) {
|
||||
LOGGER_WARNING("Failed to create recursive mutex!");
|
||||
free(vc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)))
|
||||
goto BASE_CLEANUP;
|
||||
|
||||
int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
|
||||
goto BASE_CLEANUP;
|
||||
}
|
||||
|
||||
/* Set encoder to some initial values
|
||||
*/
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
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));
|
||||
goto BASE_CLEANUP_1;
|
||||
}
|
||||
|
||||
cfg.rc_target_bitrate = 500000;
|
||||
cfg.g_w = 800;
|
||||
cfg.g_h = 600;
|
||||
cfg.g_pass = VPX_RC_ONE_PASS;
|
||||
/* FIXME If we set error resilience the app will crash due to bug in vp8.
|
||||
Perhaps vp9 has solved it?*/
|
||||
// cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
|
||||
cfg.g_lag_in_frames = 0;
|
||||
cfg.kf_min_dist = 0;
|
||||
cfg.kf_max_dist = 48;
|
||||
cfg.kf_mode = VPX_KF_AUTO;
|
||||
|
||||
rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
||||
goto BASE_CLEANUP_1;
|
||||
}
|
||||
|
||||
rc = vpx_codec_control(vc->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));
|
||||
vpx_codec_destroy(vc->encoder);
|
||||
goto BASE_CLEANUP_1;
|
||||
}
|
||||
|
||||
vc->linfts = current_time_monotonic();
|
||||
vc->lcfd = 60;
|
||||
vc->vcb.first = cb;
|
||||
vc->vcb.second = cb_data;
|
||||
vc->friend_number = friend_number;
|
||||
vc->av = av;
|
||||
|
||||
return vc;
|
||||
|
||||
BASE_CLEANUP_1:
|
||||
vpx_codec_destroy(vc->decoder);
|
||||
BASE_CLEANUP:
|
||||
pthread_mutex_destroy(vc->queue_mutex);
|
||||
rb_kill(vc->vbuf_raw);
|
||||
free(vc);
|
||||
return NULL;
|
||||
}
|
||||
void vc_kill(VCSession *vc)
|
||||
{
|
||||
if (!vc)
|
||||
return;
|
||||
|
||||
vpx_codec_destroy(vc->encoder);
|
||||
vpx_codec_destroy(vc->decoder);
|
||||
|
||||
void *p;
|
||||
|
||||
while (rb_read(vc->vbuf_raw, (void **)&p))
|
||||
free(p);
|
||||
|
||||
rb_kill(vc->vbuf_raw);
|
||||
|
||||
pthread_mutex_destroy(vc->queue_mutex);
|
||||
|
||||
LOGGER_DEBUG("Terminated video handler: %p", vc);
|
||||
free(vc);
|
||||
}
|
||||
void vc_iterate(VCSession *vc)
|
||||
{
|
||||
if (!vc)
|
||||
return;
|
||||
|
||||
struct RTPMessage *p;
|
||||
int rc;
|
||||
|
||||
pthread_mutex_lock(vc->queue_mutex);
|
||||
|
||||
if (rb_read(vc->vbuf_raw, (void **)&p)) {
|
||||
pthread_mutex_unlock(vc->queue_mutex);
|
||||
|
||||
rc = vpx_codec_decode(vc->decoder, p->data, p->len, 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(vc->decoder, &iter);
|
||||
|
||||
/* Play decoded images */
|
||||
for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
|
||||
if (vc->vcb.first)
|
||||
vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
|
||||
(const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
|
||||
dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
|
||||
|
||||
vpx_img_free(dest);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(vc->queue_mutex);
|
||||
}
|
||||
int vc_queue_message(void *vcp, struct RTPMessage *msg)
|
||||
{
|
||||
/* This function does the reconstruction of video packets.
|
||||
* See more info about video splitting in docs
|
||||
*/
|
||||
if (!vcp || !msg)
|
||||
return -1;
|
||||
|
||||
if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
|
||||
LOGGER_WARNING("Got dummy!");
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg->header.pt != rtp_TypeVideo % 128) {
|
||||
LOGGER_WARNING("Invalid payload type!");
|
||||
free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
VCSession *vc = vcp;
|
||||
|
||||
pthread_mutex_lock(vc->queue_mutex);
|
||||
free(rb_write(vc->vbuf_raw, msg));
|
||||
{
|
||||
/* Calculate time took for peer to send us this frame */
|
||||
uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
|
||||
vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
|
||||
vc->linfts = current_time_monotonic();
|
||||
}
|
||||
pthread_mutex_unlock(vc->queue_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height)
|
||||
{
|
||||
if (!vc)
|
||||
return -1;
|
||||
|
||||
vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc;
|
||||
int rc;
|
||||
|
||||
if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height)
|
||||
return 0; /* Nothing changed */
|
||||
|
||||
if (cfg.g_w == width && cfg.g_h == height) {
|
||||
/* Only bit rate changed */
|
||||
cfg.rc_target_bitrate = bit_rate;
|
||||
|
||||
rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
|
||||
* reconfiguring encoder to use resolutions greater than initially set.
|
||||
*/
|
||||
|
||||
LOGGER_DEBUG("Have to reinitialize vpx encoder on session %p", vc);
|
||||
|
||||
cfg.rc_target_bitrate = bit_rate;
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
|
||||
vpx_codec_ctx_t new_c;
|
||||
|
||||
rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8);
|
||||
|
||||
if (rc != VPX_CODEC_OK) {
|
||||
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
||||
vpx_codec_destroy(&new_c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vpx_codec_destroy(vc->encoder);
|
||||
memcpy(vc->encoder, &new_c, sizeof(new_c));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
67
toxav/video.h
Normal file
67
toxav/video.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/** video.h
|
||||
*
|
||||
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_H
|
||||
#define VIDEO_H
|
||||
|
||||
#include <vpx/vpx_decoder.h>
|
||||
#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())
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "toxav.h"
|
||||
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
struct RTPMessage;
|
||||
|
||||
typedef struct VCSession_s {
|
||||
/* encoding */
|
||||
vpx_codec_ctx_t encoder[1];
|
||||
uint32_t frame_counter;
|
||||
|
||||
/* decoding */
|
||||
vpx_codec_ctx_t decoder[1];
|
||||
void *vbuf_raw; /* Un-decoded data */
|
||||
|
||||
uint64_t linfts; /* Last received frame time stamp */
|
||||
uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
|
||||
|
||||
ToxAV *av;
|
||||
uint32_t friend_number;
|
||||
|
||||
PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
|
||||
|
||||
pthread_mutex_t queue_mutex[1];
|
||||
} VCSession;
|
||||
|
||||
VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data);
|
||||
void vc_kill(VCSession *vc);
|
||||
void vc_iterate(VCSession *vc);
|
||||
int vc_queue_message(void *vcp, struct RTPMessage *msg);
|
||||
int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height);
|
||||
|
||||
#endif /* VIDEO_H */
|
|
@ -1264,7 +1264,7 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
|
|||
if (ft->status != FILESTATUS_NOT_ACCEPTED)
|
||||
return -5;
|
||||
|
||||
if (position > ft->size) {
|
||||
if (position >= ft->size) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
|
@ -1569,7 +1569,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* seek can only be sent by the receiver to seek before resuming broken tranfers. */
|
||||
/* seek can only be sent by the receiver to seek before resuming broken transfers. */
|
||||
if (ft->status != FILESTATUS_NOT_ACCEPTED || !receive_send) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1577,7 +1577,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
|
|||
memcpy(&position, data, sizeof(position));
|
||||
net_to_host((uint8_t *) &position, sizeof(position));
|
||||
|
||||
if (position > ft->size) {
|
||||
if (position >= ft->size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -2128,6 +2128,11 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
|
|||
file_data = data + 1;
|
||||
}
|
||||
|
||||
/* Prevent more data than the filesize from being passed to clients. */
|
||||
if ((ft->transferred + file_data_length) > ft->size) {
|
||||
file_data_length = ft->size - ft->transferred;
|
||||
}
|
||||
|
||||
if (m->file_filedata)
|
||||
(*m->file_filedata)(m, i, real_filenumber, position, file_data, file_data_length, m->file_filedata_userdata);
|
||||
|
||||
|
@ -2240,7 +2245,7 @@ static void connection_status_cb(Messenger *m)
|
|||
}
|
||||
|
||||
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
|
||||
static time_t lastdump = 0;
|
||||
static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1];
|
||||
|
@ -2316,7 +2321,7 @@ void do_messenger(Messenger *m)
|
|||
do_friends(m);
|
||||
connection_status_cb(m);
|
||||
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
|
||||
if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
|
||||
|
||||
|
@ -2415,7 +2420,7 @@ void do_messenger(Messenger *m)
|
|||
}
|
||||
}
|
||||
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
}
|
||||
|
||||
/* new messenger format for load/save, more robust and forward compatible */
|
||||
|
|
|
@ -878,9 +878,9 @@ void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
static char *idpart2str(uint8_t *id, size_t len);
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
|
||||
/* refresh buckets */
|
||||
void do_Assoc(Assoc *assoc, DHT *dht)
|
||||
|
@ -974,7 +974,7 @@ void kill_Assoc(Assoc *assoc)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
|
||||
static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1];
|
||||
static char *idpart2str(uint8_t *id, size_t len)
|
||||
|
@ -1028,4 +1028,4 @@ void Assoc_status(const Assoc *assoc)
|
|||
}
|
||||
}
|
||||
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
|
|
|
@ -97,8 +97,8 @@ void do_Assoc(Assoc *assoc, DHT *dht);
|
|||
/* destroy */
|
||||
void kill_Assoc(Assoc *assoc);
|
||||
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
void Assoc_status(const Assoc *assoc);
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
|
||||
#endif /* !__ASSOC_H__ */
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
#endif
|
||||
|
||||
|
||||
struct logger {
|
||||
struct Logger {
|
||||
FILE *log_file;
|
||||
LOG_LEVEL level;
|
||||
uint64_t start_time; /* Time when lib loaded */
|
||||
|
@ -87,7 +87,7 @@ char *strtime(char *dest, size_t max_len)
|
|||
*/
|
||||
Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
|
||||
{
|
||||
#ifndef LOGGING /* Disabled */
|
||||
#ifndef TOX_LOGGER /* Disabled */
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
|
@ -96,7 +96,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
|
|||
if (!retu)
|
||||
return NULL;
|
||||
|
||||
if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) {
|
||||
if (pthread_mutex_init(retu->mutex, NULL) != 0) {
|
||||
free(retu);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
|
|||
|
||||
if (!(retu->tstr = calloc(16, sizeof (char))) ||
|
||||
!(retu->posstr = calloc(300, sizeof (char))) ||
|
||||
!(retu->msg = calloc(4096, sizeof (char))) )
|
||||
!(retu->msg = calloc(4096, sizeof (char))))
|
||||
goto FAILURE;
|
||||
|
||||
if (id) {
|
||||
|
@ -147,7 +147,7 @@ FAILURE:
|
|||
|
||||
void logger_kill(Logger *log)
|
||||
{
|
||||
#ifndef LOGGING /* Disabled */
|
||||
#ifndef TOX_LOGGER /* Disabled */
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
@ -160,7 +160,7 @@ void logger_kill(Logger *log)
|
|||
free(log->posstr);
|
||||
free(log->msg);
|
||||
|
||||
if (fclose(log->log_file) != 0 )
|
||||
if (fclose(log->log_file) != 0)
|
||||
perror("Could not close log file");
|
||||
|
||||
pthread_mutex_unlock(log->mutex);
|
||||
|
@ -177,7 +177,7 @@ void logger_kill_global(void)
|
|||
|
||||
void logger_set_global(Logger *log)
|
||||
{
|
||||
#ifndef LOGGING /* Disabled */
|
||||
#ifndef TOX_LOGGER /* Disabled */
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
@ -186,7 +186,7 @@ void logger_set_global(Logger *log)
|
|||
|
||||
Logger *logger_get_global(void)
|
||||
{
|
||||
#ifndef LOGGING /* Disabled */
|
||||
#ifndef TOX_LOGGER /* Disabled */
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
|
@ -195,17 +195,17 @@ Logger *logger_get_global(void)
|
|||
|
||||
void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...)
|
||||
{
|
||||
#ifndef LOGGING /* Disabled */
|
||||
#ifndef TOX_LOGGER /* Disabled */
|
||||
return;
|
||||
#endif
|
||||
|
||||
static const char *logger_format =
|
||||
"%s " /* Logger id string */
|
||||
"%-16s" /* Time string of format: %m:%d %H:%M:%S */
|
||||
"%u " /* Thread id */
|
||||
"%-5s " /* Logger lever string */
|
||||
"%-20s " /* File:line string */
|
||||
"- %s" /* Output message */
|
||||
"%s " /* Logger id string */
|
||||
"%-16s" /* Time string of format: %m:%d %H:%M:%S */
|
||||
"%-12u " /* Thread id */
|
||||
"%-5s " /* Logger lever string */
|
||||
"%-20s " /* File:line string */
|
||||
"- %s" /* Output message */
|
||||
WIN_CR "\n"; /* Every new print new line */
|
||||
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ typedef enum {
|
|||
LOG_ERROR
|
||||
} LOG_LEVEL;
|
||||
|
||||
typedef struct logger Logger;
|
||||
typedef struct Logger Logger;
|
||||
|
||||
/**
|
||||
* Set 'level' as the lowest printable level. If id == NULL, random number is used.
|
||||
|
@ -66,21 +66,22 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con
|
|||
|
||||
|
||||
/* To do some checks or similar only when logging, use this */
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)
|
||||
# define LOGGER_WRITE(log, level, format, ...) \
|
||||
logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__ )
|
||||
logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#else
|
||||
/* # warning "Logging disabled" */
|
||||
# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0)
|
||||
# define LOGGER_WRITE(log, level, format, ...) do {} while(0)
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
|
||||
/* To log with an logger */
|
||||
#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__ )
|
||||
#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__ )
|
||||
#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__ )
|
||||
#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__ )
|
||||
#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__ )
|
||||
#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__)
|
||||
#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__)
|
||||
#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__)
|
||||
#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__)
|
||||
#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__)
|
||||
|
||||
/* To log with the global logger */
|
||||
#define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__)
|
||||
|
|
|
@ -266,7 +266,7 @@ uint64_t current_time_monotonic(void)
|
|||
}
|
||||
|
||||
/* In case no logging */
|
||||
#ifndef LOGGING
|
||||
#ifndef TOX_LOGGER
|
||||
#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__)
|
||||
#else
|
||||
#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0
|
||||
|
@ -287,7 +287,7 @@ uint64_t current_time_monotonic(void)
|
|||
__buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \
|
||||
ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__));
|
||||
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
|
||||
/* Basic network functions:
|
||||
* Function to send packet(data) of length length to ip_port.
|
||||
|
@ -615,9 +615,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
|
|||
}
|
||||
|
||||
if (ip.family == AF_INET6) {
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
int is_dualstack =
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
set_socket_dualstack(temp->sock);
|
||||
LOGGER_DEBUG( "Dual-stack socket: %s",
|
||||
is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" );
|
||||
|
@ -628,9 +628,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
|
|||
mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02;
|
||||
mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
|
||||
mreq.ipv6mr_interface = 0;
|
||||
#ifdef LOGGING
|
||||
#ifdef TOX_LOGGER
|
||||
int res =
|
||||
#endif /* LOGGING */
|
||||
#endif /* TOX_LOGGER */
|
||||
setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
|
||||
|
||||
LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" :
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "ping_array.h"
|
||||
|
||||
#define MAX_ONION_CLIENTS 8
|
||||
#define ONION_NODE_PING_INTERVAL 20
|
||||
#define ONION_NODE_PING_INTERVAL 15
|
||||
#define ONION_NODE_TIMEOUT (ONION_NODE_PING_INTERVAL * 3)
|
||||
|
||||
/* The interval in seconds at which to tell our friends where we are */
|
||||
|
|
|
@ -192,3 +192,87 @@ int create_recursive_mutex(pthread_mutex_t *mutex)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
uint16_t size; /* Max size */
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
void **data;
|
||||
};
|
||||
|
||||
bool rb_full(const RingBuffer *b)
|
||||
{
|
||||
return (b->end + 1) % b->size == b->start;
|
||||
}
|
||||
bool rb_empty(const RingBuffer *b)
|
||||
{
|
||||
return b->end == b->start;
|
||||
}
|
||||
void *rb_write(RingBuffer *b, void *p)
|
||||
{
|
||||
void *rc = NULL;
|
||||
|
||||
if ((b->end + 1) % b->size == b->start) /* full */
|
||||
rc = b->data[b->start];
|
||||
|
||||
b->data[b->end] = p;
|
||||
b->end = (b->end + 1) % b->size;
|
||||
|
||||
if (b->end == b->start)
|
||||
b->start = (b->start + 1) % b->size;
|
||||
|
||||
return rc;
|
||||
}
|
||||
bool rb_read(RingBuffer *b, void **p)
|
||||
{
|
||||
if (b->end == b->start) { /* Empty */
|
||||
*p = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
*p = b->data[b->start];
|
||||
b->start = (b->start + 1) % b->size;
|
||||
return true;
|
||||
}
|
||||
RingBuffer *rb_new(int size)
|
||||
{
|
||||
RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
|
||||
|
||||
if (!buf) return NULL;
|
||||
|
||||
buf->size = size + 1; /* include empty elem */
|
||||
|
||||
if (!(buf->data = calloc(buf->size, sizeof(void *)))) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
void rb_kill(RingBuffer *b)
|
||||
{
|
||||
if (b) {
|
||||
free(b->data);
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
uint16_t rb_size(const RingBuffer *b)
|
||||
{
|
||||
if (rb_empty(b))
|
||||
return 0;
|
||||
|
||||
return
|
||||
b->end > b->start ?
|
||||
b->end - b->start :
|
||||
(b->size - b->start) + b->end;
|
||||
}
|
||||
uint16_t rb_data(const RingBuffer *b, void **dest)
|
||||
{
|
||||
uint16_t i = 0;
|
||||
|
||||
for (; i < rb_size(b); i++)
|
||||
dest[i] = b->data[(b->start + i) % b->size];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
|
||||
|
||||
void unix_time_update();
|
||||
uint64_t unix_time();
|
||||
|
@ -54,6 +55,18 @@ typedef int (*load_state_callback_func)(void *outer, const uint8_t *data, uint32
|
|||
int load_state(load_state_callback_func load_state_callback, void *outer,
|
||||
const uint8_t *data, uint32_t length, uint16_t cookie_inner);
|
||||
|
||||
/* Returns -1 if failed or 0 if success */
|
||||
int create_recursive_mutex(pthread_mutex_t *mutex);
|
||||
|
||||
/* Ring buffer */
|
||||
typedef struct RingBuffer RingBuffer;
|
||||
bool rb_full(const RingBuffer *b);
|
||||
bool rb_empty(const RingBuffer *b);
|
||||
void *rb_write(RingBuffer *b, void *p);
|
||||
bool rb_read(RingBuffer *b, void **p);
|
||||
RingBuffer *rb_new(int size);
|
||||
void rb_kill(RingBuffer *b);
|
||||
uint16_t rb_size(const RingBuffer *b);
|
||||
uint16_t rb_data(const RingBuffer *b, void **dest);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
|
|
@ -44,7 +44,7 @@ uint8_t i = 0; \
|
|||
} \
|
||||
} \
|
||||
} \
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t temp_pk[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t temp_sk[crypto_box_SECRETKEYBYTES];
|
||||
|
|
Loading…
Reference in New Issue
Block a user