mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Fixed several bugs and added some features
This commit is contained in:
parent
71284fabf8
commit
6a78e2e71c
|
@ -487,7 +487,8 @@ fi
|
||||||
if test "x$BUILD_AV" = "xyes"; then
|
if test "x$BUILD_AV" = "xyes"; then
|
||||||
# toxcore lib needs an global?
|
# toxcore lib needs an global?
|
||||||
# So far this works okay
|
# So far this works okay
|
||||||
AV_LIBS="$OPUS_LIBS $VPX_LIBS"
|
## What about pthread?
|
||||||
|
AV_LIBS="$OPUS_LIBS $VPX_LIBS -pthread"
|
||||||
AC_SUBST(AV_LIBS)
|
AC_SUBST(AV_LIBS)
|
||||||
|
|
||||||
AV_CFLAGS="$OPUS_CFLAGS $VPX_CFLAGS"
|
AV_CFLAGS="$OPUS_CFLAGS $VPX_CFLAGS"
|
||||||
|
|
11
libtoxav.pc
Normal file
11
libtoxav.pc
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
prefix=/usr/local
|
||||||
|
exec_prefix=${prefix}
|
||||||
|
libdir=${exec_prefix}/lib
|
||||||
|
includedir=${prefix}/include
|
||||||
|
|
||||||
|
Name: libtoxav
|
||||||
|
Description: Tox A/V library
|
||||||
|
Requires:
|
||||||
|
Version: 0.0.0
|
||||||
|
Libs: -L${libdir} -ltoxav -lopus -lvpx -lm -pthread
|
||||||
|
Cflags: -I${includedir}
|
107
toxav/media.c
107
toxav/media.c
|
@ -46,18 +46,28 @@ struct jitter_buffer {
|
||||||
uint8_t id_set;
|
uint8_t id_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int empty_queue(struct jitter_buffer *q)
|
||||||
|
{
|
||||||
|
while (q->size > 0) {
|
||||||
|
rtp_free_msg(NULL, q->queue[q->front]);
|
||||||
|
q->front++;
|
||||||
|
|
||||||
|
if (q->front == q->capacity)
|
||||||
|
q->front = 0;
|
||||||
|
|
||||||
|
q->size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->id_set = 0;
|
||||||
|
q->queue_ready = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct jitter_buffer *create_queue(int capacity)
|
struct jitter_buffer *create_queue(int capacity)
|
||||||
{
|
{
|
||||||
struct jitter_buffer *q;
|
struct jitter_buffer *q;
|
||||||
q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer), 1);
|
q = calloc(sizeof(struct jitter_buffer), 1);
|
||||||
q->queue = (RTPMessage **)calloc(sizeof(RTPMessage *), capacity);
|
q->queue = calloc(sizeof(RTPMessage *), capacity);
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < capacity; ++i) {
|
|
||||||
q->queue[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->size = 0;
|
q->size = 0;
|
||||||
q->capacity = capacity;
|
q->capacity = capacity;
|
||||||
q->front = 0;
|
q->front = 0;
|
||||||
|
@ -81,7 +91,7 @@ uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint3
|
||||||
/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
|
/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
|
||||||
RTPMessage *dequeue(struct jitter_buffer *q, int *success)
|
RTPMessage *dequeue(struct jitter_buffer *q, int *success)
|
||||||
{
|
{
|
||||||
if (q->size == 0 || q->queue_ready == 0) {
|
if (q->size == 0 || q->queue_ready == 0) { /* Empty queue */
|
||||||
q->queue_ready = 0;
|
q->queue_ready = 0;
|
||||||
*success = 0;
|
*success = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -103,13 +113,13 @@ RTPMessage *dequeue(struct jitter_buffer *q, int *success)
|
||||||
q->current_ts = next_ts;
|
q->current_ts = next_ts;
|
||||||
} else {
|
} else {
|
||||||
if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
|
if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
|
||||||
printf("nextid: %d current: %d\n", next_id, q->current_id);
|
/*printf("nextid: %d current: %d\n", next_id, q->current_id);*/
|
||||||
q->current_id = (q->current_id + 1) % MAX_SEQU_NUM;
|
q->current_id = (q->current_id + 1) % MAX_SEQU_NUM;
|
||||||
*success = 2; /* tell the decoder the packet is lost */
|
*success = 2; /* tell the decoder the packet is lost */
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
/* packet too old */
|
/* packet too old */
|
||||||
printf("packet too old\n");
|
/*printf("packet too old\n");*/
|
||||||
*success = 0;
|
*success = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -128,27 +138,12 @@ RTPMessage *dequeue(struct jitter_buffer *q, int *success)
|
||||||
return q->queue[front];
|
return q->queue[front];
|
||||||
}
|
}
|
||||||
|
|
||||||
int empty_queue(struct jitter_buffer *q)
|
|
||||||
{
|
|
||||||
while (q->size > 0) {
|
|
||||||
q->size--;
|
|
||||||
rtp_free_msg(NULL, q->queue[q->front]);
|
|
||||||
q->front++;
|
|
||||||
|
|
||||||
if (q->front == q->capacity)
|
|
||||||
q->front = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->id_set = 0;
|
|
||||||
q->queue_ready = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int queue(struct jitter_buffer *q, RTPMessage *pk)
|
int queue(struct jitter_buffer *q, RTPMessage *pk)
|
||||||
{
|
{
|
||||||
if (q->size == q->capacity) {
|
if (q->size == q->capacity) { /* Full, empty queue */
|
||||||
printf("buffer full, emptying buffer...\n");
|
|
||||||
empty_queue(q);
|
empty_queue(q);
|
||||||
|
/*rtp_free_msg(NULL, pk);*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +175,7 @@ int queue(struct jitter_buffer *q, RTPMessage *pk)
|
||||||
temp = q->queue[a];
|
temp = q->queue[a];
|
||||||
q->queue[a] = q->queue[b];
|
q->queue[a] = q->queue[b];
|
||||||
q->queue[b] = temp;
|
q->queue[b] = temp;
|
||||||
printf("had to swap\n");
|
/*printf("had to swap\n");*/
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +224,7 @@ int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t
|
||||||
int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
printf("Failed to get config: %s\n", vpx_codec_err_to_string(res));
|
fprintf(stderr, "Failed to get config: %s\n", vpx_codec_err_to_string(res));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,49 +261,43 @@ CodecState *codec_init_session ( uint32_t audio_bitrate,
|
||||||
uint16_t video_height,
|
uint16_t video_height,
|
||||||
uint32_t video_bitrate )
|
uint32_t video_bitrate )
|
||||||
{
|
{
|
||||||
CodecState *_retu = calloc(sizeof(CodecState), 1);
|
CodecState *retu = calloc(sizeof(CodecState), 1);
|
||||||
assert(_retu);
|
assert(retu);
|
||||||
|
|
||||||
_retu->audio_bitrate = audio_bitrate;
|
retu->audio_bitrate = audio_bitrate;
|
||||||
_retu->audio_sample_rate = audio_sample_rate;
|
retu->audio_sample_rate = audio_sample_rate;
|
||||||
|
|
||||||
/* Encoders */
|
/* Encoders */
|
||||||
if (!video_width || !video_height) {
|
if (!video_width || !video_height) { /* Disable video */
|
||||||
video_width = 320;
|
/*video_width = 320;
|
||||||
video_height = 240;
|
video_height = 240; */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
retu->supported_actions |= ( 0 == init_video_encoder(retu, video_width, video_height, video_bitrate) ) ? v_encoding : 0;
|
||||||
|
retu->supported_actions |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 0 == init_video_encoder(_retu, video_width, video_height, video_bitrate) )
|
retu->supported_actions |= ( 0 == init_audio_encoder(retu, audio_channels) ) ? a_encoding : 0;
|
||||||
printf("Video encoder initialized!\n");
|
retu->supported_actions |= ( 0 == init_audio_decoder(retu, audio_channels) ) ? a_decoding : 0;
|
||||||
|
|
||||||
if ( 0 == init_audio_encoder(_retu, audio_channels) )
|
return retu;
|
||||||
printf("Audio encoder initialized!\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Decoders */
|
|
||||||
if ( 0 == init_video_decoder(_retu) )
|
|
||||||
printf("Video decoder initialized!\n");
|
|
||||||
|
|
||||||
if ( 0 == init_audio_decoder(_retu, audio_channels) )
|
|
||||||
printf("Audio decoder initialized!\n");
|
|
||||||
|
|
||||||
|
|
||||||
return _retu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void codec_terminate_session ( CodecState *cs )
|
void codec_terminate_session ( CodecState *cs )
|
||||||
{
|
{
|
||||||
if ( cs->audio_encoder ) {
|
if ( cs->audio_encoder )
|
||||||
opus_encoder_destroy(cs->audio_encoder);
|
opus_encoder_destroy(cs->audio_encoder);
|
||||||
printf("Terminated encoder!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( cs->audio_decoder ) {
|
if ( cs->audio_decoder )
|
||||||
opus_decoder_destroy(cs->audio_decoder);
|
opus_decoder_destroy(cs->audio_decoder);
|
||||||
printf("Terminated decoder!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Terminate video */
|
|
||||||
|
/* TODO: Terminate video
|
||||||
|
* Do what???
|
||||||
|
*/
|
||||||
|
if ( cs->supported_actions & v_decoding )
|
||||||
vpx_codec_destroy(&cs->v_decoder);
|
vpx_codec_destroy(&cs->v_decoder);
|
||||||
|
|
||||||
|
if ( cs->supported_actions & v_encoding )
|
||||||
vpx_codec_destroy(&cs->v_encoder);
|
vpx_codec_destroy(&cs->v_encoder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,14 @@
|
||||||
/* Audio encoding/decoding */
|
/* Audio encoding/decoding */
|
||||||
#include <opus/opus.h>
|
#include <opus/opus.h>
|
||||||
|
|
||||||
|
enum _actions
|
||||||
|
{
|
||||||
|
no_actions,
|
||||||
|
a_encoding = 1 << 0,
|
||||||
|
a_decoding = 1 << 1,
|
||||||
|
v_encoding = 1 << 2,
|
||||||
|
v_decoding = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct _CodecState {
|
typedef struct _CodecState {
|
||||||
|
|
||||||
|
@ -56,12 +64,13 @@ typedef struct _CodecState {
|
||||||
/* audio decoding */
|
/* audio decoding */
|
||||||
OpusDecoder *audio_decoder;
|
OpusDecoder *audio_decoder;
|
||||||
|
|
||||||
|
uint64_t supported_actions; /* Encoding decoding etc */
|
||||||
|
|
||||||
} CodecState;
|
} CodecState;
|
||||||
|
|
||||||
typedef struct _RTPMessage RTPMessage;
|
typedef struct _RTPMessage RTPMessage;
|
||||||
|
|
||||||
struct jitter_buffer *create_queue(int capacity);
|
struct jitter_buffer *create_queue(int capacity);
|
||||||
int empty_queue(struct jitter_buffer *q);
|
|
||||||
|
|
||||||
int queue(struct jitter_buffer *q, RTPMessage *pk);
|
int queue(struct jitter_buffer *q, RTPMessage *pk);
|
||||||
RTPMessage *dequeue(struct jitter_buffer *q, int *success);
|
RTPMessage *dequeue(struct jitter_buffer *q, int *success);
|
||||||
|
|
82
toxav/msi.c
Normal file → Executable file
82
toxav/msi.c
Normal file → Executable file
|
@ -616,10 +616,15 @@ int send_message ( MSISession *session, MSIMessage *msg, uint32_t to )
|
||||||
void flush_peer_type ( MSISession *session, MSIMessage *msg, int peer_id )
|
void flush_peer_type ( MSISession *session, MSIMessage *msg, int peer_id )
|
||||||
{
|
{
|
||||||
if ( msg->calltype.header_value ) {
|
if ( msg->calltype.header_value ) {
|
||||||
if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
|
uint8_t hdrval [MSI_MAXMSG_SIZE]; /* Make sure no overflow */
|
||||||
|
|
||||||
|
memcpy(hdrval, msg->calltype.header_value, msg->calltype.size);
|
||||||
|
hdrval[msg->calltype.size] = '\0';
|
||||||
|
|
||||||
|
if ( strcmp ( ( const char * ) hdrval, CT_AUDIO_HEADER_VALUE ) == 0 ) {
|
||||||
session->call->type_peer[peer_id] = type_audio;
|
session->call->type_peer[peer_id] = type_audio;
|
||||||
|
|
||||||
} else if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
|
} else if ( strcmp ( ( const char * ) hdrval, CT_VIDEO_HEADER_VALUE ) == 0 ) {
|
||||||
session->call->type_peer[peer_id] = type_video;
|
session->call->type_peer[peer_id] = type_video;
|
||||||
} else {} /* Error */
|
} else {} /* Error */
|
||||||
} else {} /* Error */
|
} else {} /* Error */
|
||||||
|
@ -670,7 +675,7 @@ int handle_error ( MSISession *session, MSICallError errid, uint32_t to )
|
||||||
session->last_error_id = errid;
|
session->last_error_id = errid;
|
||||||
session->last_error_str = stringify_error ( errid );
|
session->last_error_str = stringify_error ( errid );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnError], session->agent_handler );
|
if ( callbacks[MSI_OnError] ) event.rise ( callbacks[MSI_OnError], session->agent_handler );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -723,12 +728,12 @@ void *handle_timeout ( void *arg )
|
||||||
uint16_t _it = 0;
|
uint16_t _it = 0;
|
||||||
|
|
||||||
for ( ; _it < _peer_count; _it++ )
|
for ( ; _it < _peer_count; _it++ )
|
||||||
msi_cancel ( arg, _peers[_it], (const uint8_t *)"Timeout" );
|
msi_cancel ( arg, _peers[_it], "Timeout" );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler );
|
if ( callbacks[MSI_OnRequestTimeout] ) callbacks[MSI_OnRequestTimeout] ( _session->agent_handler );
|
||||||
( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
|
if ( callbacks[MSI_OnEnding] ) callbacks[MSI_OnEnding ] ( _session->agent_handler );
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -864,7 +869,7 @@ int handle_recv_invite ( MSISession *session, MSIMessage *msg )
|
||||||
send_message ( session, _msg_ringing, msg->friend_id );
|
send_message ( session, _msg_ringing, msg->friend_id );
|
||||||
free_message ( _msg_ringing );
|
free_message ( _msg_ringing );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
|
if ( callbacks[MSI_OnInvite] ) event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -888,7 +893,7 @@ int handle_recv_start ( MSISession *session, MSIMessage *msg )
|
||||||
|
|
||||||
flush_peer_type ( session, msg, 0 );
|
flush_peer_type ( session, msg, 0 );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnStart], session->agent_handler );
|
if ( callbacks[MSI_OnStart] ) event.rise ( callbacks[MSI_OnStart], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -905,7 +910,7 @@ int handle_recv_reject ( MSISession *session, MSIMessage *msg )
|
||||||
free_message ( _msg_end );
|
free_message ( _msg_end );
|
||||||
|
|
||||||
event.timer_release ( session->call->request_timer_id );
|
event.timer_release ( session->call->request_timer_id );
|
||||||
event.rise ( callbacks[MSI_OnReject], session->agent_handler );
|
if ( callbacks[MSI_OnReject] ) event.rise ( callbacks[MSI_OnReject], session->agent_handler );
|
||||||
session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
|
session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -920,7 +925,7 @@ int handle_recv_cancel ( MSISession *session, MSIMessage *msg )
|
||||||
|
|
||||||
terminate_call ( session );
|
terminate_call ( session );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
|
if ( callbacks[MSI_OnCancel] ) event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -938,7 +943,7 @@ int handle_recv_end ( MSISession *session, MSIMessage *msg )
|
||||||
|
|
||||||
terminate_call ( session );
|
terminate_call ( session );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
|
if ( callbacks[MSI_OnEnd] ) event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -952,7 +957,7 @@ int handle_recv_ringing ( MSISession *session, MSIMessage *msg )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
|
session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
|
||||||
event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
|
if ( callbacks[MSI_OnRinging] ) event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -991,7 +996,7 @@ int handle_recv_starting ( MSISession *session, MSIMessage *msg )
|
||||||
|
|
||||||
flush_peer_type ( session, msg, 0 );
|
flush_peer_type ( session, msg, 0 );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
|
if ( callbacks[MSI_OnStarting] ) event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
|
||||||
event.timer_release ( session->call->ringing_timer_id );
|
event.timer_release ( session->call->ringing_timer_id );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1003,11 +1008,19 @@ int handle_recv_ending ( MSISession *session, MSIMessage *msg )
|
||||||
if ( has_call_error ( session, msg ) == 0 )
|
if ( has_call_error ( session, msg ) == 0 )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Do the callback before ending
|
||||||
|
if ( callbacks[MSI_OnEnding] ) event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Stop timer */
|
||||||
|
event.timer_release ( session->call->request_timer_id );
|
||||||
|
|
||||||
|
/* Call callback */
|
||||||
|
if ( callbacks[MSI_OnEnding] ) callbacks[MSI_OnEnding](session->agent_handler);
|
||||||
|
|
||||||
|
/* Terminate call */
|
||||||
terminate_call ( session );
|
terminate_call ( session );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int handle_recv_error ( MSISession *session, MSIMessage *msg )
|
int handle_recv_error ( MSISession *session, MSIMessage *msg )
|
||||||
|
@ -1023,7 +1036,7 @@ int handle_recv_error ( MSISession *session, MSIMessage *msg )
|
||||||
|
|
||||||
terminate_call ( session );
|
terminate_call ( session );
|
||||||
|
|
||||||
event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
|
if ( callbacks[MSI_OnEnding] ) event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1082,7 +1095,12 @@ void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16
|
||||||
|
|
||||||
if ( _msg->request.header_value ) { /* Handle request */
|
if ( _msg->request.header_value ) { /* Handle request */
|
||||||
|
|
||||||
const uint8_t *_request_value = _msg->request.header_value;
|
if ( _msg->response.size > 32 ) goto free_end;
|
||||||
|
|
||||||
|
uint8_t _request_value[32];
|
||||||
|
|
||||||
|
memcpy(_request_value, _msg->request.header_value, _msg->request.size);
|
||||||
|
_request_value[_msg->request.size] = '\0';
|
||||||
|
|
||||||
if ( same ( _request_value, stringify_request ( invite ) ) ) {
|
if ( same ( _request_value, stringify_request ( invite ) ) ) {
|
||||||
handle_recv_invite ( _session, _msg );
|
handle_recv_invite ( _session, _msg );
|
||||||
|
@ -1100,14 +1118,16 @@ void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16
|
||||||
handle_recv_end ( _session, _msg );
|
handle_recv_end ( _session, _msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else goto free_end;
|
||||||
free_message ( _msg );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ( _msg->response.header_value ) { /* Handle response */
|
} else if ( _msg->response.header_value ) { /* Handle response */
|
||||||
|
|
||||||
const uint8_t *_response_value = _msg->response.header_value;
|
if ( _msg->response.size > 32 ) goto free_end;
|
||||||
|
|
||||||
|
uint8_t _response_value[32];
|
||||||
|
|
||||||
|
memcpy(_response_value, _msg->response.header_value, _msg->response.size);
|
||||||
|
_response_value[_msg->response.size] = '\0';
|
||||||
|
|
||||||
if ( same ( _response_value, stringify_response ( ringing ) ) ) {
|
if ( same ( _response_value, stringify_response ( ringing ) ) ) {
|
||||||
handle_recv_ringing ( _session, _msg );
|
handle_recv_ringing ( _session, _msg );
|
||||||
|
@ -1120,10 +1140,8 @@ void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16
|
||||||
|
|
||||||
} else if ( same ( _response_value, stringify_response ( error ) ) ) {
|
} else if ( same ( _response_value, stringify_response ( error ) ) ) {
|
||||||
handle_recv_error ( _session, _msg );
|
handle_recv_error ( _session, _msg );
|
||||||
} else {
|
|
||||||
free_message ( _msg );
|
} else goto free_end;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Got response so cancel timer */
|
/* Got response so cancel timer */
|
||||||
if ( _session->call )
|
if ( _session->call )
|
||||||
|
@ -1131,7 +1149,7 @@ void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free_message ( _msg );
|
free_end:free_message ( _msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1286,16 +1304,16 @@ int msi_hangup ( MSISession *session )
|
||||||
if ( !session->call || session->call->state != call_active )
|
if ( !session->call || session->call->state != call_active )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
MSIMessage *_msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
|
MSIMessage *_msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
|
||||||
|
|
||||||
/* hangup for each peer */
|
/* hangup for each peer */
|
||||||
int _it = 0;
|
int _it = 0;
|
||||||
|
|
||||||
for ( ; _it < session->call->peer_count; _it ++ )
|
for ( ; _it < session->call->peer_count; _it ++ )
|
||||||
send_message ( session, _msg_ending, session->call->peers[_it] );
|
send_message ( session, _msg_end, session->call->peers[_it] );
|
||||||
|
|
||||||
|
|
||||||
free_message ( _msg_ending );
|
free_message ( _msg_end );
|
||||||
|
|
||||||
session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
|
session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
|
||||||
|
|
||||||
|
@ -1352,13 +1370,13 @@ int msi_answer ( MSISession *session, MSICallType call_type )
|
||||||
* @param reason Set optional reason header. Pass NULL if none.
|
* @param reason Set optional reason header. Pass NULL if none.
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int msi_cancel ( MSISession *session, uint32_t peer, const uint8_t *reason )
|
int msi_cancel ( MSISession *session, uint32_t peer, const char *reason )
|
||||||
{
|
{
|
||||||
assert ( session );
|
assert ( session );
|
||||||
|
|
||||||
MSIMessage *_msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
|
MSIMessage *_msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
|
||||||
|
|
||||||
if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char *)reason));
|
if ( reason ) msi_msg_set_reason(_msg_cancel, (const uint8_t*)reason, strlen(reason));
|
||||||
|
|
||||||
send_message ( session, _msg_cancel, peer );
|
send_message ( session, _msg_cancel, peer );
|
||||||
free_message ( _msg_cancel );
|
free_message ( _msg_cancel );
|
||||||
|
|
2
toxav/msi.h
Normal file → Executable file
2
toxav/msi.h
Normal file → Executable file
|
@ -206,7 +206,7 @@ int msi_answer ( MSISession *session, MSICallType call_type );
|
||||||
* @param reason Set optional reason header. Pass NULL if none.
|
* @param reason Set optional reason header. Pass NULL if none.
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int msi_cancel ( MSISession *session, uint32_t peer, const uint8_t *reason );
|
int msi_cancel ( MSISession* session, uint32_t peer, const char* reason );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
170
toxav/phone.c
Normal file → Executable file
170
toxav/phone.c
Normal file → Executable file
|
@ -428,8 +428,8 @@ void *encode_audio_thread(void *arg)
|
||||||
int frame_size = AUDIO_FRAME_SIZE;
|
int frame_size = AUDIO_FRAME_SIZE;
|
||||||
ALint sample = 0;
|
ALint sample = 0;
|
||||||
alcCaptureStart((ALCdevice *)_phone->audio_capture_device);
|
alcCaptureStart((ALCdevice *)_phone->audio_capture_device);
|
||||||
|
|
||||||
while (_phone->running_encaud) {
|
while (_phone->running_encaud) {
|
||||||
|
|
||||||
alcGetIntegerv((ALCdevice *)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
|
alcGetIntegerv((ALCdevice *)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
|
||||||
|
|
||||||
if (sample >= frame_size) {
|
if (sample >= frame_size) {
|
||||||
|
@ -437,8 +437,7 @@ void *encode_audio_thread(void *arg)
|
||||||
|
|
||||||
ret = toxav_send_audio(_phone->av, frame, frame_size);
|
ret = toxav_send_audio(_phone->av, frame, frame_size);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0) printf("Could not encode or send audio packet\n");
|
||||||
printf("Could not encode or send audio packet\n");
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
@ -734,6 +733,114 @@ ending:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void *one_threaded_audio(void *arg)
|
||||||
|
{
|
||||||
|
INFO("Started audio thread!");
|
||||||
|
av_session_t *_phone = arg;
|
||||||
|
_phone->running_decaud = 1;
|
||||||
|
|
||||||
|
//int recved_size;
|
||||||
|
//uint8_t dest [RTP_PAYLOAD_SIZE];
|
||||||
|
|
||||||
|
int frame_size = AUDIO_FRAME_SIZE;
|
||||||
|
|
||||||
|
int16_t frame[4096];
|
||||||
|
ALint sample = 0;
|
||||||
|
alcCaptureStart((ALCdevice *)_phone->audio_capture_device);
|
||||||
|
|
||||||
|
ALCdevice *dev;
|
||||||
|
ALCcontext *ctx;
|
||||||
|
ALuint source, *buffers;
|
||||||
|
dev = alcOpenDevice(NULL);
|
||||||
|
ctx = alcCreateContext(dev, NULL);
|
||||||
|
alcMakeContextCurrent(ctx);
|
||||||
|
int openal_buffers = 5;
|
||||||
|
|
||||||
|
buffers = calloc(sizeof(ALuint) * openal_buffers, 1);
|
||||||
|
alGenBuffers(openal_buffers, buffers);
|
||||||
|
alGenSources((ALuint)1, &source);
|
||||||
|
alSourcei(source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
|
ALuint buffer;
|
||||||
|
ALint ready;
|
||||||
|
|
||||||
|
uint16_t zeros[frame_size];
|
||||||
|
memset(zeros, 0, frame_size);
|
||||||
|
int16_t PCM[frame_size];
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < openal_buffers; ++i) {
|
||||||
|
alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceQueueBuffers(source, openal_buffers, buffers);
|
||||||
|
alSourcePlay(source);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "Error starting audio\n");
|
||||||
|
goto ending;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dec_frame_len;
|
||||||
|
|
||||||
|
while (_phone->running_decaud) {
|
||||||
|
|
||||||
|
// combo
|
||||||
|
alcGetIntegerv((ALCdevice *)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
|
||||||
|
|
||||||
|
// record and send
|
||||||
|
if (sample >= frame_size) {
|
||||||
|
alcCaptureSamples((ALCdevice *)_phone->audio_capture_device, frame, frame_size);
|
||||||
|
|
||||||
|
if (toxav_send_audio(_phone->av, frame, frame_size) < 0)
|
||||||
|
printf("Could not encode or send audio packet\n");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// play received
|
||||||
|
|
||||||
|
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
|
||||||
|
|
||||||
|
if (ready <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM);
|
||||||
|
|
||||||
|
/* Play the packet */
|
||||||
|
if (dec_frame_len > 0) {
|
||||||
|
alSourceUnqueueBuffers(source, 1, &buffer);
|
||||||
|
alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
|
||||||
|
int error = alGetError();
|
||||||
|
|
||||||
|
if (error != AL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "Error setting buffer %d\n", error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceQueueBuffers(source, 1, &buffer);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "Error: could not buffer audio\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &ready);
|
||||||
|
|
||||||
|
if (ready != AL_PLAYING) alSourcePlay(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ending:
|
||||||
|
_phone->running_decaud = -1;
|
||||||
|
|
||||||
|
pthread_exit ( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int phone_startmedia_loop ( ToxAv *arg )
|
int phone_startmedia_loop ( ToxAv *arg )
|
||||||
|
@ -742,7 +849,7 @@ int phone_startmedia_loop ( ToxAv *arg )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
toxav_prepare_transmission(arg);
|
toxav_prepare_transmission(arg, 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rise all threads
|
* Rise all threads
|
||||||
|
@ -759,10 +866,10 @@ int phone_startmedia_loop ( ToxAv *arg )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Always send audio */
|
/* Always send audio */
|
||||||
if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) ) {
|
/*if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) ) {
|
||||||
INFO("Error while starting encode_audio_thread()");
|
INFO("Error while starting encode_audio_thread()");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* Only checks for last peer */
|
/* Only checks for last peer */
|
||||||
if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
|
if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
|
||||||
|
@ -771,12 +878,19 @@ int phone_startmedia_loop ( ToxAv *arg )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) ) {
|
/*if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) ) {
|
||||||
INFO("Error while starting decode_audio_thread()");
|
INFO("Error while starting decode_audio_thread()");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
/* One threaded audio */
|
||||||
|
|
||||||
|
if ( 0 > event.rise(one_threaded_audio, toxav_get_agent_handler(arg)) ) {
|
||||||
|
INFO ("Shit-head");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,14 +1002,16 @@ void *callback_call_ended ( void *_arg )
|
||||||
_phone->running_encvid = 0;
|
_phone->running_encvid = 0;
|
||||||
_phone->running_decvid = 0;
|
_phone->running_decvid = 0;
|
||||||
|
|
||||||
/* Wait until all threads are done */
|
/* Wait until all threads are done
|
||||||
|
|
||||||
while ( _phone->running_encaud != -1 ||
|
while ( _phone->running_encaud != -1 ||
|
||||||
_phone->running_decaud != -1 ||
|
_phone->running_decaud != -1 ||
|
||||||
_phone->running_encvid != -1 ||
|
_phone->running_encvid != -1 ||
|
||||||
_phone->running_decvid != -1 )
|
_phone->running_decvid != -1 )
|
||||||
|
|
||||||
usleep(10000000);
|
usleep(1000000);*/
|
||||||
|
|
||||||
|
while (_phone->running_decaud != -1) usleep(1000000);
|
||||||
|
|
||||||
toxav_kill_transmission(_phone->av);
|
toxav_kill_transmission(_phone->av);
|
||||||
INFO ( "Call ended!" );
|
INFO ( "Call ended!" );
|
||||||
|
@ -977,7 +1093,7 @@ av_session_t *av_init_session()
|
||||||
if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
|
if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
|
||||||
fprintf(stderr, "Opening video_input_format failed!\n");
|
fprintf(stderr, "Opening video_input_format failed!\n");
|
||||||
//return -1;
|
//return -1;
|
||||||
return NULL;
|
goto failed_init_ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
avformat_find_stream_info(_retu->video_format_ctx, NULL);
|
avformat_find_stream_info(_retu->video_format_ctx, NULL);
|
||||||
|
@ -996,23 +1112,25 @@ av_session_t *av_init_session()
|
||||||
if (_retu->webcam_decoder == NULL) {
|
if (_retu->webcam_decoder == NULL) {
|
||||||
fprintf(stderr, "Unsupported codec!\n");
|
fprintf(stderr, "Unsupported codec!\n");
|
||||||
//return -1;
|
//return -1;
|
||||||
return NULL;
|
goto failed_init_ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_retu->webcam_decoder_ctx == NULL) {
|
if (_retu->webcam_decoder_ctx == NULL) {
|
||||||
fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
|
fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
|
||||||
//return -1;
|
//return -1;
|
||||||
return NULL;
|
goto failed_init_ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
|
if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
|
||||||
fprintf(stderr, "Opening webcam decoder failed!\n");
|
fprintf(stderr, "Opening webcam decoder failed!\n");
|
||||||
//return -1;
|
//return -1;
|
||||||
return NULL;
|
goto failed_init_ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = _retu->webcam_decoder_ctx->width;
|
width = _retu->webcam_decoder_ctx->width;
|
||||||
height = _retu->webcam_decoder_ctx->height;
|
height = _retu->webcam_decoder_ctx->height;
|
||||||
|
|
||||||
|
failed_init_ffmpeg: ;
|
||||||
#endif
|
#endif
|
||||||
uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
|
uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
tox_get_address(_retu->_messenger, _byte_address );
|
tox_get_address(_retu->_messenger, _byte_address );
|
||||||
|
@ -1023,18 +1141,18 @@ av_session_t *av_init_session()
|
||||||
|
|
||||||
/* ------------------ */
|
/* ------------------ */
|
||||||
|
|
||||||
toxav_register_callstate_callback(callback_call_started, OnStart);
|
toxav_register_callstate_callback(callback_call_started, av_OnStart);
|
||||||
toxav_register_callstate_callback(callback_call_canceled, OnCancel);
|
toxav_register_callstate_callback(callback_call_canceled, av_OnCancel);
|
||||||
toxav_register_callstate_callback(callback_call_rejected, OnReject);
|
toxav_register_callstate_callback(callback_call_rejected, av_OnReject);
|
||||||
toxav_register_callstate_callback(callback_call_ended, OnEnd);
|
toxav_register_callstate_callback(callback_call_ended, av_OnEnd);
|
||||||
toxav_register_callstate_callback(callback_recv_invite, OnInvite);
|
toxav_register_callstate_callback(callback_recv_invite, av_OnInvite);
|
||||||
|
|
||||||
toxav_register_callstate_callback(callback_recv_ringing, OnRinging);
|
toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging);
|
||||||
toxav_register_callstate_callback(callback_recv_starting, OnStarting);
|
toxav_register_callstate_callback(callback_recv_starting, av_OnStarting);
|
||||||
toxav_register_callstate_callback(callback_recv_ending, OnEnding);
|
toxav_register_callstate_callback(callback_recv_ending, av_OnEnding);
|
||||||
|
|
||||||
toxav_register_callstate_callback(callback_recv_error, OnError);
|
toxav_register_callstate_callback(callback_recv_error, av_OnError);
|
||||||
toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout);
|
toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout);
|
||||||
|
|
||||||
/* ------------------ */
|
/* ------------------ */
|
||||||
|
|
||||||
|
@ -1310,6 +1428,8 @@ int main ( int argc, char *argv [] )
|
||||||
|
|
||||||
av_session_t *_phone = av_init_session();
|
av_session_t *_phone = av_init_session();
|
||||||
|
|
||||||
|
assert ( _phone );
|
||||||
|
|
||||||
tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
|
tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
|
||||||
tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
|
tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
|
||||||
|
|
||||||
|
@ -1341,7 +1461,7 @@ int main ( int argc, char *argv [] )
|
||||||
"================================================================================\n"
|
"================================================================================\n"
|
||||||
"%s\n"
|
"%s\n"
|
||||||
"================================================================================"
|
"================================================================================"
|
||||||
, _phone->_my_public_id );
|
, trim_spaces(_phone->_my_public_id) );
|
||||||
|
|
||||||
|
|
||||||
do_phone (_phone);
|
do_phone (_phone);
|
||||||
|
|
42
toxav/rtp.c
Normal file → Executable file
42
toxav/rtp.c
Normal file → Executable file
|
@ -262,16 +262,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( _cc > 0 ) {
|
memset(_retu->csrc, 0, 16);
|
||||||
_retu->csrc = calloc (_cc, sizeof (uint32_t));
|
|
||||||
assert(_retu->csrc);
|
|
||||||
|
|
||||||
} else { /* But this should not happen ever */
|
|
||||||
/* Deallocate */
|
|
||||||
free(_retu);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_retu->marker_payloadt = *_it;
|
_retu->marker_payloadt = *_it;
|
||||||
++_it;
|
++_it;
|
||||||
|
@ -362,14 +353,12 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
|
||||||
_it += 4;
|
_it += 4;
|
||||||
U32_to_bytes( _it, header->ssrc);
|
U32_to_bytes( _it, header->ssrc);
|
||||||
|
|
||||||
if ( header->csrc ) {
|
|
||||||
uint8_t _x;
|
uint8_t _x;
|
||||||
|
|
||||||
for ( _x = 0; _x < _cc; _x++ ) {
|
for ( _x = 0; _x < _cc; _x++ ) {
|
||||||
_it += 4;
|
_it += 4;
|
||||||
U32_to_bytes( _it, header->csrc[_x]);
|
U32_to_bytes( _it, header->csrc[_x]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return _it + 4;
|
return _it + 4;
|
||||||
}
|
}
|
||||||
|
@ -424,18 +413,10 @@ RTPHeader *build_header ( RTPSession *session )
|
||||||
_retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */
|
_retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */
|
||||||
_retu->ssrc = session->ssrc;
|
_retu->ssrc = session->ssrc;
|
||||||
|
|
||||||
if ( session->cc > 0 ) {
|
|
||||||
_retu->csrc = calloc(session->cc, sizeof (uint32_t));
|
|
||||||
assert(_retu->csrc);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for ( i = 0; i < session->cc; i++ ) {
|
for ( i = 0; i < session->cc; i++ )
|
||||||
_retu->csrc[i] = session->csrc[i];
|
_retu->csrc[i] = session->csrc[i];
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_retu->csrc = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
|
_retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
|
||||||
|
|
||||||
|
@ -480,9 +461,7 @@ RTPMessage *msg_parse ( uint16_t sequnum, const uint8_t *data, int length )
|
||||||
_retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
|
_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 );
|
_from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
|
||||||
} else { /* Error */
|
} else { /* Error */
|
||||||
free (_retu->ext_header);
|
rtp_free_msg(NULL, _retu);
|
||||||
free (_retu->header);
|
|
||||||
free (_retu);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -545,7 +524,7 @@ int rtp_handle_packet ( void *object, IP_Port ip_port, uint8_t *data, uint32_t l
|
||||||
_decrypted_length = decrypt_data_symmetric(
|
_decrypted_length = decrypt_data_symmetric(
|
||||||
(uint8_t *)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain );
|
(uint8_t *)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain );
|
||||||
|
|
||||||
if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */
|
if ( _decrypted_length == -1 ) return -1; /* This packet is not encrypted properly */
|
||||||
|
|
||||||
/* Otherwise, if decryption is ok with new cycle, set new cycle
|
/* Otherwise, if decryption is ok with new cycle, set new cycle
|
||||||
*/
|
*/
|
||||||
|
@ -554,7 +533,7 @@ int rtp_handle_packet ( void *object, IP_Port ip_port, uint8_t *data, uint32_t l
|
||||||
_decrypted_length = decrypt_data_symmetric(
|
_decrypted_length = decrypt_data_symmetric(
|
||||||
(uint8_t *)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
|
(uint8_t *)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
|
||||||
|
|
||||||
if ( !_decrypted_length ) return -1; /* This is just an error */
|
if ( _decrypted_length == -1 ) return -1; /* This is just an error */
|
||||||
|
|
||||||
/* A new cycle setting. */
|
/* A new cycle setting. */
|
||||||
memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_secretbox_NONCEBYTES);
|
memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_secretbox_NONCEBYTES);
|
||||||
|
@ -801,16 +780,11 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat
|
||||||
void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
|
void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
|
||||||
{
|
{
|
||||||
if ( !session ) {
|
if ( !session ) {
|
||||||
free ( msg->header->csrc );
|
|
||||||
|
|
||||||
if ( msg->ext_header ) {
|
if ( msg->ext_header ) {
|
||||||
free ( msg->ext_header->table );
|
free ( msg->ext_header->table );
|
||||||
free ( msg->ext_header );
|
free ( msg->ext_header );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( session->csrc != msg->header->csrc )
|
|
||||||
free ( msg->header->csrc );
|
|
||||||
|
|
||||||
if ( msg->ext_header && session->ext_header != msg->ext_header ) {
|
if ( msg->ext_header && session->ext_header != msg->ext_header ) {
|
||||||
free ( msg->ext_header->table );
|
free ( msg->ext_header->table );
|
||||||
free ( msg->ext_header );
|
free ( msg->ext_header );
|
||||||
|
@ -920,12 +894,18 @@ int rtp_terminate_session ( RTPSession *session, Messenger *messenger )
|
||||||
|
|
||||||
custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL);
|
custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL);
|
||||||
|
|
||||||
|
rtp_release_session_recv(session);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&session->mutex);
|
||||||
|
|
||||||
free ( session->ext_header );
|
free ( session->ext_header );
|
||||||
free ( session->csrc );
|
free ( session->csrc );
|
||||||
free ( session->decrypt_nonce );
|
free ( session->decrypt_nonce );
|
||||||
free ( session->encrypt_nonce );
|
free ( session->encrypt_nonce );
|
||||||
free ( session->nonce_cycle );
|
free ( session->nonce_cycle );
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&session->mutex);
|
||||||
|
|
||||||
pthread_mutex_destroy(&session->mutex);
|
pthread_mutex_destroy(&session->mutex);
|
||||||
|
|
||||||
/* And finally free session */
|
/* And finally free session */
|
||||||
|
|
2
toxav/rtp.h
Normal file → Executable file
2
toxav/rtp.h
Normal file → Executable file
|
@ -47,7 +47,7 @@ typedef struct _RTPHeader {
|
||||||
uint16_t sequnum; /* Sequence Number */
|
uint16_t sequnum; /* Sequence Number */
|
||||||
uint32_t timestamp; /* Timestamp */
|
uint32_t timestamp; /* Timestamp */
|
||||||
uint32_t ssrc; /* SSRC */
|
uint32_t ssrc; /* SSRC */
|
||||||
uint32_t *csrc; /* CSRC's table */
|
uint32_t csrc[16]; /* CSRC's table */
|
||||||
uint32_t length; /* Length of the header in payload string. */
|
uint32_t length; /* Length of the header in payload string. */
|
||||||
|
|
||||||
} RTPHeader;
|
} RTPHeader;
|
||||||
|
|
113
toxav/toxav.c
Normal file → Executable file
113
toxav/toxav.c
Normal file → Executable file
|
@ -241,7 +241,7 @@ int toxav_cancel ( ToxAv *av, const char *reason )
|
||||||
return ErrorNoCall;
|
return ErrorNoCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
return msi_cancel(av->msi_session, 0, (const uint8_t *)reason);
|
return msi_cancel(av->msi_session, 0, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,7 +269,7 @@ int toxav_stop_call ( ToxAv *av )
|
||||||
* @retval 0 Success.
|
* @retval 0 Success.
|
||||||
* @retval ToxAvError On error.
|
* @retval ToxAvError On error.
|
||||||
*/
|
*/
|
||||||
int toxav_prepare_transmission ( ToxAv *av )
|
int toxav_prepare_transmission ( ToxAv* av, int support_video )
|
||||||
{
|
{
|
||||||
assert(av->msi_session);
|
assert(av->msi_session);
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ int toxav_prepare_transmission ( ToxAv *av )
|
||||||
return ErrorStartingAudioRtp;
|
return ErrorStartingAudioRtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( support_video ) {
|
||||||
av->rtp_sessions[video_index] = rtp_init_session (
|
av->rtp_sessions[video_index] = rtp_init_session (
|
||||||
type_video,
|
type_video,
|
||||||
av->messenger,
|
av->messenger,
|
||||||
|
@ -308,7 +309,7 @@ int toxav_prepare_transmission ( ToxAv *av )
|
||||||
fprintf(stderr, "Error while starting video RTP session!\n");
|
fprintf(stderr, "Error while starting video RTP session!\n");
|
||||||
return ErrorStartingVideoRtp;
|
return ErrorStartingVideoRtp;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ErrorNone;
|
return ErrorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,21 +323,20 @@ int toxav_prepare_transmission ( ToxAv *av )
|
||||||
*/
|
*/
|
||||||
int toxav_kill_transmission ( ToxAv *av )
|
int toxav_kill_transmission ( ToxAv *av )
|
||||||
{
|
{
|
||||||
/* Both sessions should be active at any time */
|
if ( av->rtp_sessions[audio_index] && -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
|
||||||
if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
|
|
||||||
return ErrorNoTransmission;
|
|
||||||
|
|
||||||
|
|
||||||
if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
|
|
||||||
fprintf(stderr, "Error while terminating audio RTP session!\n");
|
fprintf(stderr, "Error while terminating audio RTP session!\n");
|
||||||
return ErrorTerminatingAudioRtp;
|
return ErrorTerminatingAudioRtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
|
if ( av->rtp_sessions[video_index] && -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
|
||||||
fprintf(stderr, "Error while terminating video RTP session!\n");
|
fprintf(stderr, "Error while terminating video RTP session!\n");
|
||||||
return ErrorTerminatingVideoRtp;
|
return ErrorTerminatingVideoRtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
av->rtp_sessions[audio_index] = NULL;
|
||||||
|
av->rtp_sessions[video_index] = NULL;
|
||||||
|
|
||||||
|
|
||||||
return ErrorNone;
|
return ErrorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,13 +424,14 @@ inline__ int toxav_recv_video ( ToxAv *av, vpx_image_t **output)
|
||||||
|
|
||||||
uint8_t packet [RTP_PAYLOAD_SIZE];
|
uint8_t packet [RTP_PAYLOAD_SIZE];
|
||||||
int recved_size = 0;
|
int recved_size = 0;
|
||||||
|
int error;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
|
recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
|
||||||
|
|
||||||
if (recved_size > 0) {
|
if (recved_size > 0)
|
||||||
printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
|
fprintf(stderr, "Error decoding: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
|
||||||
}
|
|
||||||
} while (recved_size > 0);
|
} while (recved_size > 0);
|
||||||
|
|
||||||
vpx_codec_iter_t iter = NULL;
|
vpx_codec_iter_t iter = NULL;
|
||||||
|
@ -456,7 +457,7 @@ inline__ int toxav_recv_video ( ToxAv *av, vpx_image_t **output)
|
||||||
inline__ int toxav_send_video ( ToxAv *av, vpx_image_t *input)
|
inline__ int toxav_send_video ( ToxAv *av, vpx_image_t *input)
|
||||||
{
|
{
|
||||||
if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
|
if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
|
||||||
printf("could not encode video frame\n");
|
fprintf(stderr, "Could not encode video frame\n");
|
||||||
return ErrorInternal;
|
return ErrorInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +501,6 @@ inline__ int toxav_recv_audio ( ToxAv *av, int frame_size, int16_t *dest )
|
||||||
int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
|
int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
|
||||||
|
|
||||||
if ( recved_size == ErrorAudioPacketLost ) {
|
if ( recved_size == ErrorAudioPacketLost ) {
|
||||||
printf("Lost packet\n");
|
|
||||||
return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
|
return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
|
||||||
} else if ( recved_size ) {
|
} else if ( recved_size ) {
|
||||||
return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
|
return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
|
||||||
|
@ -550,6 +550,24 @@ int toxav_get_peer_transmission_type ( ToxAv *av, int peer )
|
||||||
return av->msi_session->call->type_peer[peer];
|
return av->msi_session->call->type_peer[peer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get id of peer participating in conversation
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @param peer peer index
|
||||||
|
* @return int
|
||||||
|
* @retval ToxAvError No peer id
|
||||||
|
*/
|
||||||
|
int toxav_get_peer_id ( ToxAv* av, int peer )
|
||||||
|
{
|
||||||
|
assert(av->msi_session);
|
||||||
|
|
||||||
|
if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
|
||||||
|
return ErrorInternal;
|
||||||
|
|
||||||
|
return av->msi_session->call->peers[peer];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get reference to an object that is handling av session.
|
* @brief Get reference to an object that is handling av session.
|
||||||
*
|
*
|
||||||
|
@ -560,3 +578,68 @@ void *toxav_get_agent_handler ( ToxAv *av )
|
||||||
{
|
{
|
||||||
return av->agent_handler;
|
return av->agent_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is video encoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
inline__ int toxav_video_encoding ( ToxAv* av )
|
||||||
|
{
|
||||||
|
return av->cs->supported_actions & v_encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is video decoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
inline__ int toxav_video_decoding ( ToxAv* av )
|
||||||
|
{
|
||||||
|
return av->cs->supported_actions & v_decoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is audio encoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
inline__ int toxav_audio_encoding ( ToxAv* av )
|
||||||
|
{
|
||||||
|
return av->cs->supported_actions & a_encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is audio decoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
inline__ int toxav_audio_decoding ( ToxAv* av )
|
||||||
|
{
|
||||||
|
return av->cs->supported_actions & a_decoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get messenger handle
|
||||||
|
*
|
||||||
|
* @param av Handler.
|
||||||
|
* @return Tox*
|
||||||
|
*/
|
||||||
|
inline__ Tox* toxav_get_tox ( ToxAv* av )
|
||||||
|
{
|
||||||
|
return (Tox*)av->messenger;
|
||||||
|
}
|
86
toxav/toxav.h
Normal file → Executable file
86
toxav/toxav.h
Normal file → Executable file
|
@ -57,20 +57,20 @@ typedef struct Tox Tox;
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/* Requests */
|
/* Requests */
|
||||||
OnInvite,
|
av_OnInvite,
|
||||||
OnStart,
|
av_OnStart,
|
||||||
OnCancel,
|
av_OnCancel,
|
||||||
OnReject,
|
av_OnReject,
|
||||||
OnEnd,
|
av_OnEnd,
|
||||||
|
|
||||||
/* Responses */
|
/* Responses */
|
||||||
OnRinging,
|
av_OnRinging,
|
||||||
OnStarting,
|
av_OnStarting,
|
||||||
OnEnding,
|
av_OnEnding,
|
||||||
|
|
||||||
/* Protocol */
|
/* Protocol */
|
||||||
OnError,
|
av_OnError,
|
||||||
OnRequestTimeout
|
av_OnRequestTimeout
|
||||||
|
|
||||||
} ToxAvCallbackID;
|
} ToxAvCallbackID;
|
||||||
|
|
||||||
|
@ -98,9 +98,8 @@ typedef enum {
|
||||||
ErrorAudioPacketLost = -6, /* Indicating packet loss */
|
ErrorAudioPacketLost = -6, /* Indicating packet loss */
|
||||||
ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
|
ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
|
||||||
ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
|
ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
|
||||||
ErrorNoTransmission = -9, /* Returned in toxav_kill_transmission() */
|
ErrorTerminatingAudioRtp = -9, /* Returned in toxav_kill_transmission() */
|
||||||
ErrorTerminatingAudioRtp = -10, /* Returned in toxav_kill_transmission() */
|
ErrorTerminatingVideoRtp = -10, /* Returned in toxav_kill_transmission() */
|
||||||
ErrorTerminatingVideoRtp = -11, /* Returned in toxav_kill_transmission() */
|
|
||||||
|
|
||||||
} ToxAvError;
|
} ToxAvError;
|
||||||
|
|
||||||
|
@ -205,11 +204,12 @@ int toxav_stop_call(ToxAv *av);
|
||||||
* @brief Must be call before any RTP transmission occurs.
|
* @brief Must be call before any RTP transmission occurs.
|
||||||
*
|
*
|
||||||
* @param av Handler.
|
* @param av Handler.
|
||||||
|
* @param support_video Is video supported ? 1 : 0
|
||||||
* @return int
|
* @return int
|
||||||
* @retval 0 Success.
|
* @retval 0 Success.
|
||||||
* @retval ToxAvError On error.
|
* @retval ToxAvError On error.
|
||||||
*/
|
*/
|
||||||
int toxav_prepare_transmission(ToxAv *av);
|
int toxav_prepare_transmission(ToxAv *av, int support_video);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call this at the end of the transmission.
|
* @brief Call this at the end of the transmission.
|
||||||
|
@ -281,6 +281,16 @@ int toxav_send_audio ( ToxAv *av, const int16_t *frame, int frame_size);
|
||||||
*/
|
*/
|
||||||
int toxav_get_peer_transmission_type ( ToxAv *av, int peer );
|
int toxav_get_peer_transmission_type ( ToxAv *av, int peer );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get id of peer participating in conversation
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @param peer peer index
|
||||||
|
* @return int
|
||||||
|
* @retval ToxAvError No peer id
|
||||||
|
*/
|
||||||
|
int toxav_get_peer_id ( ToxAv* av, int peer );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get reference to an object that is handling av session.
|
* @brief Get reference to an object that is handling av session.
|
||||||
*
|
*
|
||||||
|
@ -289,4 +299,52 @@ int toxav_get_peer_transmission_type ( ToxAv *av, int peer );
|
||||||
*/
|
*/
|
||||||
void *toxav_get_agent_handler ( ToxAv *av );
|
void *toxav_get_agent_handler ( ToxAv *av );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is video encoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
int toxav_video_encoding ( ToxAv* av );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is video decoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
int toxav_video_decoding ( ToxAv* av );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is audio encoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
int toxav_audio_encoding ( ToxAv* av );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is audio decoding supported
|
||||||
|
*
|
||||||
|
* @param av Handler
|
||||||
|
* @return int
|
||||||
|
* @retval 1 Yes.
|
||||||
|
* @retval 0 No.
|
||||||
|
*/
|
||||||
|
int toxav_audio_decoding ( ToxAv* av );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get messenger handle
|
||||||
|
*
|
||||||
|
* @param av Handler.
|
||||||
|
* @return Tox*
|
||||||
|
*/
|
||||||
|
Tox* toxav_get_tox ( ToxAv* av );
|
||||||
|
|
||||||
#endif /* __TOXAV */
|
#endif /* __TOXAV */
|
Loading…
Reference in New Issue
Block a user