More av cleanup

This commit is contained in:
mannol 2014-11-29 13:42:19 +01:00
parent 975ce25af0
commit e62ded3a6d
13 changed files with 546 additions and 656 deletions

View File

@ -220,8 +220,8 @@ void register_callbacks(ToxAv *av, void *data)
toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data); toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data);
toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data); toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data);
toxav_register_audio_callback(callback_audio, NULL); toxav_register_audio_callback(av, callback_audio, NULL);
toxav_register_video_callback(callback_video, NULL); toxav_register_video_callback(av, callback_video, NULL);
} }

View File

@ -126,8 +126,8 @@ void register_callbacks(ToxAv *av, void *data)
toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
toxav_register_audio_callback(callback_audio, NULL); toxav_register_audio_callback(av, callback_audio, NULL);
toxav_register_video_callback(callback_video, NULL); toxav_register_video_callback(av, callback_video, NULL);
} }
/*************************************************************************************************/ /*************************************************************************************************/

View File

@ -27,6 +27,7 @@
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/util.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -50,14 +51,10 @@
/* FIXME: Might not be enough */ /* FIXME: Might not be enough */
#define VIDEO_DECODE_BUFFER_SIZE 20 #define VIDEO_DECODE_BUFFER_SIZE 20
#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } #define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; }
#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
typedef ARRAY(uint8_t) Payload; typedef ARRAY(uint8_t) Payload;
static PAIR(CSVideoCallback, void *) vpcallback;
static PAIR(CSAudioCallback, void *) apcallback;
typedef struct { typedef struct {
uint16_t size; /* Max size */ uint16_t size; /* Max size */
uint16_t start; uint16_t start;
@ -319,24 +316,12 @@ static int init_audio_encoder(CSSession *cs)
return 0; return 0;
} }
static float calculate_sum_sq (int16_t *n, uint16_t k)
{
float result = 0;
uint16_t i = 0;
for ( ; i < k; i ++) result += (float) (n[i] * n[i]);
return result;
}
/* PUBLIC */ /* PUBLIC */
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
{ {
if (!cs || !length || length > cs->max_video_frame_size) { if (!cs || !length || length > cs->max_video_frame_size) {
LOGGER_ERROR("Invalid CodecState or video frame size: %u", length); LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
return -1; return cs_ErrorSplittingVideoPayload;
} }
cs->split_video_frame[0] = cs->frameid_out++; cs->split_video_frame[0] = cs->frameid_out++;
@ -375,43 +360,23 @@ const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
void cs_do(CSSession *cs) 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; if (!cs) return;
pthread_mutex_lock(cs->queue_mutex);
if (!cs->active) {
pthread_mutex_unlock(cs->queue_mutex);
return;
}
/*
/* Iterate over whole buffers and call playback callback * /
if (cs->abuf_ready) while (!DecodedAudioBuffer_empty(cs->abuf_ready)) {
DecodedAudio* p;
DecodedAudioBuffer_read(cs->abuf_ready, &p);
if (apcallback.first)
apcallback.first(cs->agent, cs->call_idx, p->data, p->size, apcallback.second);
free(p);
}
if (cs->vbuf_ready) while (!DecodedVideoBuffer_empty(cs->vbuf_ready)) {
vpx_image_t* p;
DecodedVideoBuffer_read(cs->vbuf_ready, &p);
if (vpcallback.first)
vpcallback.first(cs->agent, cs->call_idx, p, vpcallback.second);
vpx_img_free(p);
}
*/
Payload *p; Payload *p;
int rc; int rc;
pthread_mutex_lock(cs->queue_mutex);
if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) { if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) {
/* Decode audio */ /* Decode audio */
buffer_read(cs->abuf_raw, &p); buffer_read(cs->abuf_raw, &p);
/* Leave space for (possibly) other thread to queue more data after we read it here */
pthread_mutex_unlock(cs->queue_mutex);
uint16_t fsize = (cs->audio_decoder_channels * uint16_t fsize = (cs->audio_decoder_channels *
(cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000); (cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
int16_t tmp[fsize]; int16_t tmp[fsize];
@ -421,15 +386,20 @@ void cs_do(CSSession *cs)
if (rc < 0) if (rc < 0)
LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
else else if (cs->acb.first)
/* Play */ /* Play */
apcallback.first(cs->agent, cs->call_idx, tmp, rc, apcallback.second); 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)) { if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
/* Decode video */ /* Decode video */
buffer_read(cs->vbuf_raw, &p); 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); rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
free(p); free(p);
@ -441,27 +411,17 @@ void cs_do(CSSession *cs)
/* Play decoded images */ /* Play decoded images */
for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
vpcallback.first(cs->agent, cs->call_idx, dest, vpcallback.second); if (cs->vcb.first)
cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second);
vpx_img_free(dest); vpx_img_free(dest);
} }
} }
return;
} }
pthread_mutex_unlock(cs->queue_mutex); pthread_mutex_unlock(cs->queue_mutex);
} }
void cs_register_audio_callback(CSAudioCallback cb, void *data)
{
apcallback.first = cb;
apcallback.second = data;
}
void cs_register_video_callback(CSVideoCallback cb, void *data)
{
vpcallback.first = cb;
vpcallback.second = data;
}
int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) 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; vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
@ -470,7 +430,7 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig
return 0; return 0;
if (width * height > cs->max_width * cs->max_height) if (width * height > cs->max_width * cs->max_height)
return -1; return cs_ErrorSettingVideoResolution;
LOGGER_DEBUG("New video resolution: %u %u", width, height); LOGGER_DEBUG("New video resolution: %u %u", width, height);
cfg.g_w = width; cfg.g_w = width;
@ -479,7 +439,7 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig
if ( rc != VPX_CODEC_OK) { if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
return -1; return cs_ErrorSettingVideoResolution;
} }
return 0; return 0;
@ -498,7 +458,7 @@ int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
if ( rc != VPX_CODEC_OK) { if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
return -1; return cs_ErrorSettingVideoBitrate;
} }
return 0; return 0;
@ -513,6 +473,11 @@ CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer,
return NULL; return NULL;
} }
if (create_recursive_mutex(cs->queue_mutex) != 0) {
LOGGER_WARNING("Failed to create recursive mutex!");
return NULL;
}
if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
LOGGER_WARNING("Jitter buffer creaton failed!"); LOGGER_WARNING("Jitter buffer creaton failed!");
goto error; goto error;
@ -529,38 +494,22 @@ CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer,
cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? a_encoding : 0; cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0;
cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? a_decoding : 0; cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0;
if ( !(cs->capabilities & a_encoding) || !(cs->capabilities & a_decoding) ) goto error; if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error;
if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error; if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error;
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0) goto error;
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
if (pthread_mutexattr_destroy(&attr) != 0)
LOGGER_WARNING("Failed to destroy mutex attribute!");
goto error;
}
if (pthread_mutex_init(cs->queue_mutex, &attr) != 0) {
pthread_mutexattr_destroy(&attr);
goto error;
}
if ((cs->support_video = has_video)) { if ((cs->support_video = has_video)) {
cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
cs_self->max_video_height, cs_self->video_bitrate) ) ? v_encoding : 0; cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0;
cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? v_decoding : 0; cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0;
if ( !(cs->capabilities & v_encoding) || !(cs->capabilities & v_decoding) ) goto error; 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->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
@ -569,17 +518,14 @@ CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer,
if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
} }
if (pthread_mutexattr_destroy(&attr) != 0)
LOGGER_WARNING("Failed to destroy mutex attribute!");
cs->active = 1;
return cs; return cs;
error: error:
LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
pthread_mutex_destroy(cs->queue_mutex);
buffer_free(cs->abuf_raw); buffer_free(cs->abuf_raw);
if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
@ -588,9 +534,9 @@ error:
if (has_video) { if (has_video) {
if ( cs->capabilities & v_decoding ) vpx_codec_destroy(&cs->v_decoder); if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder);
if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder); if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder);
buffer_free(cs->vbuf_raw); buffer_free(cs->vbuf_raw);
@ -608,13 +554,7 @@ void cs_kill(CSSession *cs)
{ {
if (!cs) return; if (!cs) return;
/* Lock running mutex and signal that cs is no longer active */ /* queue_message will not be called since it's unregistered before cs_kill is called */
pthread_mutex_lock(cs->queue_mutex);
cs->active = 0;
/* Wait threads to close */
pthread_mutex_unlock(cs->queue_mutex);
pthread_mutex_destroy(cs->queue_mutex); pthread_mutex_destroy(cs->queue_mutex);
@ -624,10 +564,10 @@ void cs_kill(CSSession *cs)
if ( cs->audio_decoder ) if ( cs->audio_decoder )
opus_decoder_destroy(cs->audio_decoder); opus_decoder_destroy(cs->audio_decoder);
if ( cs->capabilities & v_decoding ) if ( cs->capabilities & cs_VideoDecoding )
vpx_codec_destroy(&cs->v_decoder); vpx_codec_destroy(&cs->v_decoder);
if ( cs->capabilities & v_encoding ) if ( cs->capabilities & cs_VideoEncoding )
vpx_codec_destroy(&cs->v_encoder); vpx_codec_destroy(&cs->v_encoder);
jbuf_free(cs->j_buf); jbuf_free(cs->j_buf);
@ -639,43 +579,23 @@ void cs_kill(CSSession *cs)
free(cs); free(cs);
} }
void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration)
{
cs->EVAD_tolerance = treshold > frame_duration ? treshold / frame_duration : frame_duration;
}
int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy)
{
float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size;
if ( frame_energy > energy) {
cs->EVAD_tolerance_cr = cs->EVAD_tolerance; /* Reset counter */
return 1;
}
if ( cs->EVAD_tolerance_cr ) {
cs->EVAD_tolerance_cr --;
return 1;
}
return 0;
}
/* Called from RTP */ /* Called from RTP */
void queue_message(RTPSession *session, RTPMessage *msg) 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; CSSession *cs = session->cs;
if (!cs || !cs->active) return; if (!cs) return;
/* Audio */ /* Audio */
if (session->payload_type == type_audio % 128) { if (session->payload_type == msi_TypeAudio % 128) {
jbuf_write(cs->j_buf, msg); jbuf_write(cs->j_buf, msg);
pthread_mutex_lock(cs->queue_mutex);
int success = 0; int success = 0;
while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
@ -698,13 +618,13 @@ void queue_message(RTPSession *session, RTPMessage *msg)
} }
if (p) { if (p) {
pthread_mutex_lock(cs->queue_mutex);
buffer_write(cs->abuf_raw, p); buffer_write(cs->abuf_raw, p);
pthread_mutex_unlock(cs->queue_mutex);
} else { } else {
LOGGER_WARNING("Allocation failed! Program might misbehave!"); LOGGER_WARNING("Allocation failed! Program might misbehave!");
} }
} }
pthread_mutex_unlock(cs->queue_mutex);
} }
/* Video */ /* Video */
else { else {

View File

@ -42,16 +42,33 @@
/* Audio encoding/decoding */ /* Audio encoding/decoding */
#include <opus.h> #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 (*CSAudioCallback) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data); typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
typedef enum _CsCapabilities { /**
a_encoding = 1 << 0, * Codec capabilities
a_decoding = 1 << 1, */
v_encoding = 1 << 2, typedef enum {
v_decoding = 1 << 3 cs_AudioEncoding = 1 << 0,
} CsCapabilities; 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 { typedef struct _CSSession {
/* VIDEO /* VIDEO
@ -122,16 +139,21 @@ typedef struct _CSSession {
uint64_t capabilities; /* supports*/ uint64_t capabilities; /* supports*/
/* Callbacks */
PAIR(CSAudioCallback, void*) acb;
PAIR(CSVideoCallback, void*) vcb;
/* Buffering */ /* Buffering */
void *abuf_raw, *vbuf_raw; /* Un-decoded data */ void *abuf_raw, *vbuf_raw; /* Un-decoded data */
_Bool active;
pthread_mutex_t queue_mutex[1]; pthread_mutex_t queue_mutex[1];
void *agent; /* Pointer to ToxAv */ void *agent; /* Pointer to ToxAv */
int32_t call_idx; int32_t call_idx;
} CSSession; } 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); 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); void cs_kill(CSSession *cs);
int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length); int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length);
@ -142,19 +164,12 @@ const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size);
*/ */
void cs_do(CSSession *cs); void cs_do(CSSession *cs);
void cs_register_audio_callback(CSAudioCallback cb, void *data);
void cs_register_video_callback(CSVideoCallback cb, void *data);
/* Reconfigure video encoder; return 0 on success or -1 on failure. */ /* 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_resolution(CSSession *cs, uint16_t width, uint16_t height);
int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate); int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate);
/* Calculate energy and return 1 if has voice, 0 if not */
int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy);
void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration);
/* Internal. Called from rtp_handle_message */ /* Internal. Called from rtp_handle_message */
void queue_message(RTPSession *session, RTPMessage *msg); void queue_message(RTPSession *session, RTPMessage *msg);
#endif /* _CODEC_H_ */ #endif /* _CODEC_H_ */

View File

@ -112,10 +112,10 @@ typedef struct _MSIMessage {
static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i)
{ {
if ( s->callbacks[i].function ) { if ( s->callbacks[i].first ) {
LOGGER_DEBUG("Invoking callback function: %d", i); LOGGER_DEBUG("Invoking callback function: %d", i);
s->callbacks[i].function( s->agent_handler, c, s->callbacks[i].data ); s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second );
} }
} }
@ -791,7 +791,7 @@ static void handle_remote_connection_change(Messenger *messenger, int friend_num
for ( ; i < session->calls[j]->peer_count; i ++ ) for ( ; i < session->calls[j]->peer_count; i ++ )
if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) { if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
invoke_callback(session, j, MSI_OnPeerTimeout); invoke_callback(session, j, msi_OnPeerTimeout);
terminate_call(session, session->calls[j]); terminate_call(session, session->calls[j]);
LOGGER_DEBUG("Remote: %d timed out!", friend_num); LOGGER_DEBUG("Remote: %d timed out!", friend_num);
return; /* TODO: On group calls change behaviour */ return; /* TODO: On group calls change behaviour */
@ -820,7 +820,7 @@ static void handle_timeout ( Timer *timer )
if (call) { if (call) {
LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx);
invoke_callback(timer->session, timer->call_idx, MSI_OnRequestTimeout); invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout);
msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out");
} }
} }
@ -840,7 +840,7 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *
if ( call ) { if ( call ) {
if ( call->peers[0] == (uint32_t)msg->friend_id ) { if ( call->peers[0] == (uint32_t)msg->friend_id ) {
if (call->state == call_inviting) { if (call->state == msi_CallInviting) {
/* The glare case. A calls B when at the same time /* The glare case. A calls B when at the same time
* B calls A. Who has advantage is set bey calculating * B calls A. Who has advantage is set bey calculating
* 'bigger' Call id and then that call id is being used in * 'bigger' Call id and then that call id is being used in
@ -864,7 +864,7 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *
} else { } else {
return 0; /* Wait for ringing from peer */ return 0; /* Wait for ringing from peer */
} }
} else if (call->state == call_active) { } else if (call->state == msi_CallActive) {
/* Request for media change; call callback and send starting response */ /* Request for media change; call callback and send starting response */
if (flush_peer_csettings(call, msg, 0) != 0) { /**/ if (flush_peer_csettings(call, msg, 0) != 0) { /**/
LOGGER_WARNING("Peer sent invalid csetting!"); LOGGER_WARNING("Peer sent invalid csetting!");
@ -872,9 +872,9 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *
return 0; return 0;
} }
LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == type_audio ? "audio" : "video"); LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == msi_TypeAudio ? "audio" : "video");
send_reponse(session, call, starting, msg->friend_id); send_reponse(session, call, starting, msg->friend_id);
invoke_callback(session, call->call_idx, MSI_OnPeerCSChange); invoke_callback(session, call->call_idx, msi_OnPeerCSChange);
return 1; return 1;
} }
} else { } else {
@ -898,12 +898,12 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *
} }
memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) );
call->state = call_starting; call->state = msi_CallStarting;
add_peer( call, msg->friend_id); add_peer( call, msg->friend_id);
flush_peer_csettings ( call, msg, 0 ); flush_peer_csettings ( call, msg, 0 );
send_reponse(session, call, ringing, msg->friend_id); send_reponse(session, call, ringing, msg->friend_id);
invoke_callback(session, call->call_idx, MSI_OnInvite); invoke_callback(session, call->call_idx, msi_OnInvite);
return 1; return 1;
} }
@ -919,8 +919,8 @@ static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *m
LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id );
call->state = call_active; call->state = msi_CallActive;
invoke_callback(session, call->call_idx, MSI_OnStart); invoke_callback(session, call->call_idx, msi_OnStart);
return 1; return 1;
} }
@ -933,7 +933,7 @@ static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *
LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx);
invoke_callback(session, call->call_idx, MSI_OnReject); invoke_callback(session, call->call_idx, msi_OnReject);
send_reponse(session, call, ending, msg->friend_id); send_reponse(session, call, ending, msg->friend_id);
terminate_call(session, call); terminate_call(session, call);
@ -952,7 +952,7 @@ static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *
LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx);
invoke_callback(session, call->call_idx, MSI_OnCancel); invoke_callback(session, call->call_idx, msi_OnCancel);
terminate_call ( session, call ); terminate_call ( session, call );
return 1; return 1;
@ -967,7 +967,7 @@ static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg
LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx);
invoke_callback(session, call->call_idx, MSI_OnEnd); invoke_callback(session, call->call_idx, msi_OnEnd);
send_reponse(session, call, ending, msg->friend_id); send_reponse(session, call, ending, msg->friend_id);
terminate_call ( session, call ); terminate_call ( session, call );
@ -993,7 +993,7 @@ static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage
call->ringing_timer_id = timer_alloc call->ringing_timer_id = timer_alloc
( session, handle_timeout, call->call_idx, call->ringing_tout_ms ); ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
invoke_callback(session, call->call_idx, MSI_OnRinging); invoke_callback(session, call->call_idx, msi_OnRinging);
return 1; return 1;
} }
static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg )
@ -1003,16 +1003,16 @@ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage
return 0; return 0;
} }
if ( call->state == call_active ) { /* Change media */ if ( call->state == msi_CallActive ) { /* Change media */
LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
invoke_callback(session, call->call_idx, MSI_OnSelfCSChange); invoke_callback(session, call->call_idx, msi_OnSelfCSChange);
} else if ( call->state == call_inviting ) { } else if ( call->state == msi_CallInviting ) {
LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
call->state = call_active; call->state = msi_CallActive;
MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); MSIMessage *msg_start = msi_new_message ( TypeRequest, start );
send_message ( session, call, msg_start, msg->friend_id ); send_message ( session, call, msg_start, msg->friend_id );
@ -1023,7 +1023,7 @@ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage
/* This is here in case of glare */ /* This is here in case of glare */
timer_release(session->timer_handler, call->ringing_timer_id); timer_release(session->timer_handler, call->ringing_timer_id);
invoke_callback(session, call->call_idx, MSI_OnStart); invoke_callback(session, call->call_idx, msi_OnStart);
} else { } else {
LOGGER_ERROR("Invalid call state"); LOGGER_ERROR("Invalid call state");
terminate_call(session, call ); terminate_call(session, call );
@ -1043,14 +1043,13 @@ static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *
LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx );
invoke_callback(session, call->call_idx, MSI_OnEnd); invoke_callback(session, call->call_idx, msi_OnEnd);
terminate_call ( session, call ); terminate_call ( session, call );
return 1; return 1;
} }
static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
{ {
if ( !call ) { if ( !call ) {
LOGGER_WARNING("Handling 'error' on non-existing call!"); LOGGER_WARNING("Handling 'error' on non-existing call!");
return -1; return -1;
@ -1058,7 +1057,7 @@ static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *m
LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
invoke_callback(session, call->call_idx, MSI_OnEnd); invoke_callback(session, call->call_idx, msi_OnEnd);
/* Handle error accordingly */ /* Handle error accordingly */
if ( msg->reason.exists ) { if ( msg->reason.exists ) {
@ -1127,7 +1126,7 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t
msg->friend_id = source; msg->friend_id = source;
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
/* Find what call */ /* Find what call */
MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
@ -1187,7 +1186,7 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t
free ( msg ); free ( msg );
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
} }
@ -1195,8 +1194,8 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t
/********** User functions **********/ /********** User functions **********/
void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata ) void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
{ {
session->callbacks[id].function = callback; session->callbacks[id].first = callback;
session->callbacks[id].data = userdata; session->callbacks[id].second = userdata;
} }
@ -1224,17 +1223,6 @@ MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
goto error; goto error;
} }
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0 ||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0 ||
pthread_mutex_init(&retu->mutex, &attr) != 0 ) {
LOGGER_ERROR("Failed to init mutex! Program might misbehave!");
goto error;
}
retu->timer_handler = calloc(1, sizeof(TimerHandler)); retu->timer_handler = calloc(1, sizeof(TimerHandler));
if (retu->timer_handler == NULL) { if (retu->timer_handler == NULL) {
@ -1250,6 +1238,11 @@ MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
goto error; goto error;
} }
if (create_recursive_mutex(retu->mutex) != 0) {
LOGGER_ERROR("Failed to init mutex! Program might misbehave");
goto error;
}
retu->messenger_handle = messenger; retu->messenger_handle = messenger;
retu->agent_handler = NULL; retu->agent_handler = NULL;
retu->max_calls = max_calls; retu->max_calls = max_calls;
@ -1265,7 +1258,10 @@ MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
return retu; return retu;
error: error:
free(retu->timer_handler); if (retu->timer_handler) {
free(((TimerHandler *)retu->timer_handler)->timers);
free(retu->timer_handler);
}
free(retu->calls); free(retu->calls);
free(retu); free(retu);
return NULL; return NULL;
@ -1279,15 +1275,11 @@ int msi_kill ( MSISession *session )
return -1; return -1;
} }
pthread_mutex_lock(&session->mutex);
m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
pthread_mutex_unlock(&session->mutex); pthread_mutex_lock(session->mutex);
int _status = 0; /* Cancel active calls */
/* If have calls, cancel them */
int32_t idx = 0; int32_t idx = 0;
for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) { for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) {
/* Cancel all? */ /* Cancel all? */
uint16_t _it = 0; uint16_t _it = 0;
@ -1296,13 +1288,14 @@ int msi_kill ( MSISession *session )
*/ */
msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" ); msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
} }
pthread_mutex_destroy(&session->mutex); free ( session->calls );
pthread_mutex_unlock(session->mutex);
pthread_mutex_destroy(session->mutex);
LOGGER_DEBUG("Terminated session: %p", session); LOGGER_DEBUG("Terminated session: %p", session);
free ( session->calls );
free ( session ); free ( session );
return _status; return 0;
} }
int msi_invite ( MSISession *session, int msi_invite ( MSISession *session,
@ -1311,7 +1304,7 @@ int msi_invite ( MSISession *session,
uint32_t rngsec, uint32_t rngsec,
uint32_t friend_id ) uint32_t friend_id )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
@ -1321,17 +1314,17 @@ int msi_invite ( MSISession *session,
for (; i < session->max_calls; i ++) for (; i < session->max_calls; i ++)
if (session->calls[i] && session->calls[i]->peers[0] == friend_id) { if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
LOGGER_ERROR("Already in a call with friend %d", friend_id); LOGGER_ERROR("Already in a call with friend %d", friend_id);
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorAlreadyInCallWithPeer;
} }
MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */
if ( !call ) { if ( !call ) {
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
LOGGER_ERROR("Cannot handle more calls"); LOGGER_ERROR("Cannot handle more calls");
return -1; return msi_ErrorReachedCallLimit;
} }
*call_index = call->call_idx; *call_index = call->call_idx;
@ -1348,32 +1341,32 @@ int msi_invite ( MSISession *session,
send_message ( session, call, msg_invite, friend_id ); send_message ( session, call, msg_invite, friend_id );
free( msg_invite ); free( msg_invite );
call->state = call_inviting; call->state = msi_CallInviting;
call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout ); call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout );
LOGGER_DEBUG("Invite sent"); LOGGER_DEBUG("Invite sent");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_hangup ( MSISession *session, int32_t call_index ) int msi_hangup ( MSISession *session, int32_t call_index )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
LOGGER_ERROR("Invalid call index!"); LOGGER_ERROR("Invalid call index!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
if ( session->calls[call_index]->state != call_active ) { if ( session->calls[call_index]->state != msi_CallActive ) {
LOGGER_ERROR("No call with such index or call is not active!"); LOGGER_ERROR("Call is not active!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorInvalidState;
} }
MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); MSIMessage *msg_end = msi_new_message ( TypeRequest, end );
@ -1384,28 +1377,34 @@ int msi_hangup ( MSISession *session, int32_t call_index )
for ( ; it < session->calls[call_index]->peer_count; it ++ ) for ( ; it < session->calls[call_index]->peer_count; it ++ )
send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] ); send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
session->calls[call_index]->state = call_hanged_up; session->calls[call_index]->state = msi_CallOver;
free ( msg_end ); free ( msg_end );
session->calls[call_index]->request_timer_id = session->calls[call_index]->request_timer_id =
timer_alloc ( session, handle_timeout, call_index, m_deftout ); timer_alloc ( session, handle_timeout, call_index, m_deftout );
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ) int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index); LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
LOGGER_ERROR("Invalid call index!"); LOGGER_ERROR("Invalid call index!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
if ( session->calls[call_index]->state != msi_CallStarting ) {
LOGGER_ERROR("Call is in invalid state!");
pthread_mutex_unlock(session->mutex);
return msi_ErrorInvalidState;
}
MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting );
session->calls[call_index]->csettings_local = *csettings; session->calls[call_index]->csettings_local = *csettings;
@ -1415,23 +1414,29 @@ int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *cs
send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
free ( msg_starting ); free ( msg_starting );
session->calls[call_index]->state = call_active; session->calls[call_index]->state = msi_CallActive;
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
LOGGER_ERROR("Invalid call index!"); LOGGER_ERROR("Invalid call index!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
if ( session->calls[call_index]->state != msi_CallInviting ) {
LOGGER_ERROR("Call is in invalid state!");
pthread_mutex_unlock(session->mutex);
return msi_ErrorInvalidState;
}
MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel );
/* FIXME */ /* FIXME */
@ -1453,22 +1458,28 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c
free ( msg_cancel ); free ( msg_cancel );
terminate_call ( session, session->calls[call_index] ); terminate_call ( session, session->calls[call_index] );
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
LOGGER_ERROR("Invalid call index!"); LOGGER_ERROR("Invalid call index!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
if ( session->calls[call_index]->state != msi_CallStarting ) {
LOGGER_ERROR("Call is in invalid state!");
pthread_mutex_unlock(session->mutex);
return msi_ErrorInvalidState;
}
MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject ); MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
/* FIXME */ /* FIXME */
@ -1490,50 +1501,50 @@ int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
free ( msg_reject ); free ( msg_reject );
session->calls[call_index]->state = call_hanged_up; session->calls[call_index]->state = msi_CallOver;
session->calls[call_index]->request_timer_id = session->calls[call_index]->request_timer_id =
timer_alloc ( session, handle_timeout, call_index, m_deftout ); timer_alloc ( session, handle_timeout, call_index, m_deftout );
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_stopcall ( MSISession *session, int32_t call_index ) int msi_stopcall ( MSISession *session, int32_t call_index )
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
/* just terminate it */ /* just terminate it */
terminate_call ( session, session->calls[call_index] ); terminate_call ( session, session->calls[call_index] );
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings) int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
LOGGER_DEBUG("Changing media on call: %d", call_index); LOGGER_DEBUG("Changing media on call: %d", call_index);
if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
LOGGER_ERROR("Invalid call index!"); LOGGER_ERROR("Invalid call index!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorNoCall;
} }
MSICall *call = session->calls[call_index]; MSICall *call = session->calls[call_index];
if ( call->state != call_active ) { if ( call->state != msi_CallActive ) {
LOGGER_ERROR("Call is not active!"); LOGGER_ERROR("Call is not active!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return msi_ErrorInvalidState;
} }
MSICSettings *local = &call->csettings_local; MSICSettings *local = &call->csettings_local;
@ -1548,7 +1559,7 @@ int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSett
local->audio_sample_rate == csettings->audio_sample_rate && local->audio_sample_rate == csettings->audio_sample_rate &&
local->audio_channels == csettings->audio_channels ) { local->audio_channels == csettings->audio_channels ) {
LOGGER_ERROR("Call is already set accordingly!"); LOGGER_ERROR("Call is already set accordingly!");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return -1;
} }
@ -1562,14 +1573,14 @@ int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSett
LOGGER_DEBUG("Request for media change sent"); LOGGER_DEBUG("Request for media change sent");
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
void msi_do(MSISession *session) void msi_do(MSISession *session)
{ {
pthread_mutex_lock(&session->mutex); pthread_mutex_lock(session->mutex);
TimerHandler *timer = session->timer_handler; TimerHandler *timer = session->timer_handler;
@ -1586,5 +1597,5 @@ void msi_do(MSISession *session)
timer_release(timer, id); timer_release(timer, id);
} }
pthread_mutex_unlock(&session->mutex); pthread_mutex_unlock(session->mutex);
} }

View File

@ -25,6 +25,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <pthread.h> #include <pthread.h>
#include "codec.h"
#include "../toxcore/Messenger.h" #include "../toxcore/Messenger.h"
typedef uint8_t MSICallIDType[12]; typedef uint8_t MSICallIDType[12];
@ -35,8 +36,8 @@ typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg );
* Call type identifier. Also used as rtp callback prefix. * Call type identifier. Also used as rtp callback prefix.
*/ */
typedef enum { typedef enum {
type_audio = 192, msi_TypeAudio = 192,
type_video msi_TypeVideo
} MSICallType; } MSICallType;
@ -44,11 +45,11 @@ typedef enum {
* Call state identifiers. * Call state identifiers.
*/ */
typedef enum { typedef enum {
call_inviting, /* when sending call invite */ msi_CallInviting, /* when sending call invite */
call_starting, /* when getting call invite */ msi_CallStarting, /* when getting call invite */
call_active, msi_CallActive,
call_hold, msi_CallHold,
call_hanged_up msi_CallOver
} MSICallState; } MSICallState;
@ -74,26 +75,27 @@ typedef struct _MSICodecSettings {
* Callbacks ids that handle the states * Callbacks ids that handle the states
*/ */
typedef enum { typedef enum {
MSI_OnInvite, /* Incoming call */ msi_OnInvite, /* Incoming call */
MSI_OnRinging, /* When peer is ready to accept/reject the call */ msi_OnRinging, /* When peer is ready to accept/reject the call */
MSI_OnStart, /* Call (RTP transmission) started */ msi_OnStart, /* Call (RTP transmission) started */
MSI_OnCancel, /* The side that initiated call canceled invite */ msi_OnCancel, /* The side that initiated call canceled invite */
MSI_OnReject, /* The side that was invited rejected the call */ msi_OnReject, /* The side that was invited rejected the call */
MSI_OnEnd, /* Call that was active ended */ msi_OnEnd, /* Call that was active ended */
MSI_OnRequestTimeout, /* When the requested action didn't get response in specified time */ msi_OnRequestTimeout, /* When the requested action didn't get response in specified time */
MSI_OnPeerTimeout, /* Peer timed out; stop the call */ msi_OnPeerTimeout, /* Peer timed out; stop the call */
MSI_OnPeerCSChange, /* Peer requested Csettings change */ msi_OnPeerCSChange, /* Peer requested Csettings change */
MSI_OnSelfCSChange /* Csettings change confirmation */ msi_OnSelfCSChange /* Csettings change confirmation */
} MSICallbackID; } MSICallbackID;
/** /**
* Callbacks container * Errors
*/ */
typedef struct _MSICallbackCont { typedef enum {
MSICallbackType function; msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
void *data; msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
} MSICallbackCont; msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
} MSIError;
/** /**
* The call struct. * The call struct.
@ -135,10 +137,10 @@ typedef struct _MSISession {
uint32_t frequ; uint32_t frequ;
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
pthread_mutex_t mutex; pthread_mutex_t mutex[1];
void *timer_handler; void *timer_handler;
MSICallbackCont callbacks[10]; /* Callbacks used by this session */ PAIR(MSICallbackType, void*) callbacks[10];
} MSISession; } MSISession;
/** /**

View File

@ -70,30 +70,30 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
return NULL; return NULL;
} }
RTPHeader *_retu = calloc(1, sizeof (RTPHeader)); RTPHeader *retu = calloc(1, sizeof (RTPHeader));
if ( !_retu ) { if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL; return NULL;
} }
memcpy(&_retu->sequnum, payload, sizeof(_retu->sequnum)); memcpy(&retu->sequnum, payload, sizeof(retu->sequnum));
_retu->sequnum = ntohs(_retu->sequnum); retu->sequnum = ntohs(retu->sequnum);
const uint8_t *_it = payload + 2; const uint8_t *it = payload + 2;
_retu->flags = *_it; retu->flags = *it;
++_it; ++it;
/* This indicates if the first 2 bits are valid. /* This indicates if the first 2 bits are valid.
* Now it may happen that this is out of order but * Now it may happen that this is out of order but
* it cuts down chances of parsing some invalid value * it cuts down chances of parsing some invalid value
*/ */
if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ) { if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
/* Deallocate */ /* Deallocate */
LOGGER_WARNING("Invalid version!"); LOGGER_WARNING("Invalid version!");
free(_retu); free(retu);
return NULL; return NULL;
} }
@ -101,38 +101,37 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
* Added a check for the size of the header little sooner so * 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 * I don't need to parse the other stuff if it's bad
*/ */
uint8_t _cc = GET_FLAG_CSRCC ( _retu ); uint8_t cc = GET_FLAG_CSRCC ( retu );
int _length = 12 /* Minimum header len */ + ( _cc * 4 ); int total = 12 /* Minimum header len */ + ( cc * 4 );
if ( length < _length ) { if ( length < total ) {
/* Deallocate */ /* Deallocate */
LOGGER_WARNING("Length invalid!"); LOGGER_WARNING("Length invalid!");
free(_retu); free(retu);
return NULL; return NULL;
} }
memset(_retu->csrc, 0, 16 * sizeof (uint32_t)); memset(retu->csrc, 0, 16 * sizeof (uint32_t));
_retu->marker_payloadt = *_it; retu->marker_payloadt = *it;
++_it; ++it;
_retu->length = _length; retu->length = total;
memcpy(&_retu->timestamp, _it, sizeof(_retu->timestamp)); memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
_retu->timestamp = ntohl(_retu->timestamp); retu->timestamp = ntohl(retu->timestamp);
_it += 4; it += 4;
memcpy(&_retu->ssrc, _it, sizeof(_retu->ssrc)); memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
_retu->ssrc = ntohl(_retu->ssrc); retu->ssrc = ntohl(retu->ssrc);
uint8_t _x; uint8_t x;
for ( x = 0; x < cc; x++ ) {
for ( _x = 0; _x < _cc; _x++ ) { it += 4;
_it += 4; memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
memcpy(&_retu->csrc[_x], _it, sizeof(_retu->csrc[_x])); retu->csrc[x] = ntohl(retu->csrc[x]);
_retu->csrc[_x] = ntohl(_retu->csrc[_x]);
} }
return _retu; return retu;
} }
/** /**
@ -140,47 +139,46 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
*/ */
RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
{ {
const uint8_t *_it = payload; const uint8_t *it = payload;
RTPExtHeader *_retu = calloc(1, sizeof (RTPExtHeader)); RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader));
if ( !_retu ) { if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL; return NULL;
} }
uint16_t _ext_length; uint16_t ext_length;
memcpy(&_ext_length, _it, sizeof(_ext_length)); memcpy(&ext_length, it, sizeof(ext_length));
_ext_length = ntohs(_ext_length); ext_length = ntohs(ext_length);
_it += 2; it += 2;
if ( length < ( _ext_length * sizeof(uint32_t) ) ) { if ( length < ( ext_length * sizeof(uint32_t) ) ) {
LOGGER_WARNING("Length invalid!"); LOGGER_WARNING("Length invalid!");
free(_retu); free(retu);
return NULL; return NULL;
} }
_retu->length = _ext_length; retu->length = ext_length;
memcpy(&_retu->type, _it, sizeof(_retu->type)); memcpy(&retu->type, it, sizeof(retu->type));
_retu->type = ntohs(_retu->type); retu->type = ntohs(retu->type);
_it += 2; it += 2;
if ( !(_retu->table = calloc(_ext_length, sizeof (uint32_t))) ) { if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(_retu); free(retu);
return NULL; return NULL;
} }
uint16_t _x; uint16_t x;
for ( x = 0; x < ext_length; x++ ) {
for ( _x = 0; _x < _ext_length; _x++ ) { it += 4;
_it += 4; memcpy(&(retu->table[x]), it, sizeof(retu->table[x]));
memcpy(&(_retu->table[_x]), _it, sizeof(_retu->table[_x])); retu->table[x] = ntohl(retu->table[x]);
_retu->table[_x] = ntohl(_retu->table[_x]);
} }
return _retu; return retu;
} }
/** /**
@ -188,8 +186,8 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
*/ */
uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
{ {
uint8_t _cc = GET_FLAG_CSRCC ( header ); uint8_t cc = GET_FLAG_CSRCC ( header );
uint8_t *_it = payload; uint8_t *it = payload;
uint16_t sequnum; uint16_t sequnum;
uint32_t timestamp; uint32_t timestamp;
uint32_t ssrc; uint32_t ssrc;
@ -198,30 +196,29 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
/* Add sequence number first */ /* Add sequence number first */
sequnum = htons(header->sequnum); sequnum = htons(header->sequnum);
memcpy(_it, &sequnum, sizeof(sequnum)); memcpy(it, &sequnum, sizeof(sequnum));
_it += 2; it += 2;
*_it = header->flags; *it = header->flags;
++_it; ++it;
*_it = header->marker_payloadt; *it = header->marker_payloadt;
++_it; ++it;
timestamp = htonl(header->timestamp); timestamp = htonl(header->timestamp);
memcpy(_it, &timestamp, sizeof(timestamp)); memcpy(it, &timestamp, sizeof(timestamp));
_it += 4; it += 4;
ssrc = htonl(header->ssrc); ssrc = htonl(header->ssrc);
memcpy(_it, &ssrc, sizeof(ssrc)); memcpy(it, &ssrc, sizeof(ssrc));
uint8_t _x; uint8_t x;
for ( x = 0; x < cc; x++ ) {
for ( _x = 0; _x < _cc; _x++ ) { it += 4;
_it += 4; csrc = htonl(header->csrc[x]);
csrc = htonl(header->csrc[_x]); memcpy(it, &csrc, sizeof(csrc));
memcpy(_it, &csrc, sizeof(csrc));
} }
return _it + 4; return it + 4;
} }
/** /**
@ -229,29 +226,29 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
*/ */
uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
{ {
uint8_t *_it = payload; uint8_t *it = payload;
uint16_t length; uint16_t length;
uint16_t type; uint16_t type;
uint32_t entry; uint32_t entry;
length = htons(header->length); length = htons(header->length);
memcpy(_it, &length, sizeof(length)); memcpy(it, &length, sizeof(length));
_it += 2; it += 2;
type = htons(header->type); type = htons(header->type);
memcpy(_it, &type, sizeof(type)); memcpy(it, &type, sizeof(type));
_it -= 2; /* Return to 0 position */ it -= 2; /* Return to 0 position */
if ( header->table ) { if ( header->table ) {
uint16_t _x;
uint16_t x;
for ( _x = 0; _x < header->length; _x++ ) { for ( x = 0; x < header->length; x++ ) {
_it += 4; it += 4;
entry = htonl(header->table[_x]); entry = htonl(header->table[x]);
memcpy(_it, &entry, sizeof(entry)); memcpy(it, &entry, sizeof(entry));
} }
} }
return _it + 4; return it + 4;
} }
/** /**
@ -259,32 +256,31 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
*/ */
RTPHeader *build_header ( RTPSession *session ) RTPHeader *build_header ( RTPSession *session )
{ {
RTPHeader *_retu = calloc ( 1, sizeof (RTPHeader) ); RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) );
if ( !_retu ) { if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL; return NULL;
} }
ADD_FLAG_VERSION ( _retu, session->version ); ADD_FLAG_VERSION ( retu, session->version );
ADD_FLAG_PADDING ( _retu, session->padding ); ADD_FLAG_PADDING ( retu, session->padding );
ADD_FLAG_EXTENSION ( _retu, session->extension ); ADD_FLAG_EXTENSION ( retu, session->extension );
ADD_FLAG_CSRCC ( _retu, session->cc ); ADD_FLAG_CSRCC ( retu, session->cc );
ADD_SETTING_MARKER ( _retu, session->marker ); ADD_SETTING_MARKER ( retu, session->marker );
ADD_SETTING_PAYLOAD ( _retu, session->payload_type ); ADD_SETTING_PAYLOAD ( retu, session->payload_type );
_retu->sequnum = session->sequnum; retu->sequnum = session->sequnum;
_retu->timestamp = current_time_monotonic(); /* milliseconds */ retu->timestamp = current_time_monotonic(); /* milliseconds */
_retu->ssrc = session->ssrc; retu->ssrc = session->ssrc;
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];
_retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
return _retu; return retu;
} }
@ -294,47 +290,47 @@ RTPHeader *build_header ( RTPSession *session )
*/ */
RTPMessage *msg_parse ( const uint8_t *data, int length ) RTPMessage *msg_parse ( const uint8_t *data, int length )
{ {
RTPMessage *_retu = calloc(1, sizeof (RTPMessage)); RTPMessage *retu = calloc(1, sizeof (RTPMessage));
_retu->header = extract_header ( data, length ); /* It allocates memory and all */ retu->header = extract_header ( data, length ); /* It allocates memory and all */
if ( !_retu->header ) { if ( !retu->header ) {
LOGGER_WARNING("Header failed to extract!"); LOGGER_WARNING("Header failed to extract!");
free(_retu); free(retu);
return NULL; return NULL;
} }
uint16_t _from_pos = _retu->header->length; uint16_t from_pos = retu->header->length;
_retu->length = length - _from_pos; retu->length = length - from_pos;
if ( GET_FLAG_EXTENSION ( _retu->header ) ) { if ( GET_FLAG_EXTENSION ( retu->header ) ) {
_retu->ext_header = extract_ext_header ( data + _from_pos, length ); retu->ext_header = extract_ext_header ( data + from_pos, length );
if ( _retu->ext_header ) { if ( retu->ext_header ) {
_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 */
LOGGER_WARNING("Ext Header failed to extract!"); LOGGER_WARNING("Ext Header failed to extract!");
rtp_free_msg(NULL, _retu); rtp_free_msg(NULL, retu);
return NULL; return NULL;
} }
} else { } else {
_retu->ext_header = NULL; retu->ext_header = NULL;
} }
if ( length - _from_pos <= MAX_RTP_SIZE ) if ( length - from_pos <= MAX_RTP_SIZE )
memcpy ( _retu->data, data + _from_pos, length - _from_pos ); memcpy ( retu->data, data + from_pos, length - from_pos );
else { else {
LOGGER_WARNING("Invalid length!"); LOGGER_WARNING("Invalid length!");
rtp_free_msg(NULL, _retu); rtp_free_msg(NULL, retu);
return NULL; return NULL;
} }
_retu->next = NULL; retu->next = NULL;
return _retu; return retu;
} }
/** /**
@ -342,28 +338,28 @@ RTPMessage *msg_parse ( const uint8_t *data, int length )
*/ */
int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length )
{ {
RTPSession *_session = object; RTPSession *session = object;
RTPMessage *_msg; RTPMessage *msg;
if ( !_session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */ if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */
LOGGER_WARNING("No session or invalid length of received buffer!"); LOGGER_WARNING("No session or invalid length of received buffer!");
return -1; return -1;
} }
_msg = msg_parse ( data + 1, length - 1 ); msg = msg_parse ( data + 1, length - 1 );
if ( !_msg ) { if ( !msg ) {
LOGGER_WARNING("Could not parse message!"); LOGGER_WARNING("Could not parse message!");
return -1; return -1;
} }
/* Check if message came in late */ /* Check if message came in late */
if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ if ( check_late_message(session, msg) < 0 ) { /* Not late */
_session->rsequnum = _msg->header->sequnum; session->rsequnum = msg->header->sequnum;
_session->timestamp = _msg->header->timestamp; session->timestamp = msg->header->timestamp;
} }
queue_message(_session, _msg); queue_message(session, msg);
return 0; return 0;
} }
@ -378,30 +374,30 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t
return NULL; return NULL;
} }
uint8_t *_from_pos; uint8_t *from_pos;
RTPMessage *_retu = calloc(1, sizeof (RTPMessage)); RTPMessage *retu = calloc(1, sizeof (RTPMessage));
if ( !_retu ) { if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL; return NULL;
} }
/* Sets header values and copies the extension header in _retu */ /* Sets header values and copies the extension header in retu */
_retu->header = build_header ( session ); /* It allocates memory and all */ retu->header = build_header ( session ); /* It allocates memory and all */
_retu->ext_header = session->ext_header; retu->ext_header = session->ext_header;
uint32_t _total_length = length + _retu->header->length + 1; uint32_t total_length = length + retu->header->length + 1;
_retu->data[0] = session->prefix; retu->data[0] = session->prefix;
if ( _retu->ext_header ) { if ( retu->ext_header ) {
_total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); 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_header ( retu->header, retu->data + 1 );
_from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); from_pos = add_ext_header ( retu->ext_header, from_pos + 1 );
} else { } else {
_from_pos = add_header ( _retu->header, _retu->data + 1 ); from_pos = add_header ( retu->header, retu->data + 1 );
} }
/* /*
@ -409,14 +405,14 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t
* Of course if any * Of course if any
*/ */
/* Appends _data on to _retu->_data */ /* Appends data on to retu->data */
memcpy ( _from_pos, data, length ); memcpy ( from_pos, data, length );
_retu->length = _total_length; retu->length = total_length;
_retu->next = NULL; retu->next = NULL;
return _retu; return retu;
} }
@ -430,7 +426,7 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat
if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) {
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
rtp_free_msg ( session, msg ); rtp_free_msg ( session, msg );
return -1; return rtp_ErrorSending;
} }
@ -461,52 +457,52 @@ void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
{ {
RTPSession *_retu = calloc(1, sizeof(RTPSession)); RTPSession *retu = calloc(1, sizeof(RTPSession));
if ( !_retu ) { if ( !retu ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
return NULL; return NULL;
} }
if ( -1 == custom_lossy_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu)) { if ( -1 == custom_lossy_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, retu)) {
LOGGER_ERROR("Error setting custom register handler for rtp session"); LOGGER_ERROR("Error setting custom register handler for rtp session");
free(_retu); free(retu);
return NULL; return NULL;
} }
LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num);
_retu->version = RTP_VERSION; /* It's always 2 */ retu->version = RTP_VERSION; /* It's always 2 */
_retu->padding = 0; /* If some additional data is needed about the packet */ retu->padding = 0; /* If some additional data is needed about the packet */
_retu->extension = 0; /* If extension to header is needed */ retu->extension = 0; /* If extension to header is needed */
_retu->cc = 1; /* Amount of contributors */ retu->cc = 1; /* Amount of contributors */
_retu->csrc = NULL; /* Container */ retu->csrc = NULL; /* Container */
_retu->ssrc = random_int(); retu->ssrc = random_int();
_retu->marker = 0; retu->marker = 0;
_retu->payload_type = payload_type % 128; retu->payload_type = payload_type % 128;
_retu->dest = friend_num; retu->dest = friend_num;
_retu->rsequnum = _retu->sequnum = 0; retu->rsequnum = retu->sequnum = 0;
_retu->ext_header = NULL; /* When needed allocate */ retu->ext_header = NULL; /* When needed allocate */
if ( !(_retu->csrc = calloc(1, sizeof (uint32_t))) ) { if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!"); LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(_retu); free(retu);
return NULL; return NULL;
} }
_retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
/* Also set payload type as prefix */ /* Also set payload type as prefix */
_retu->prefix = payload_type; retu->prefix = payload_type;
/* /*
* *
*/ */
return _retu; return retu;
} }
void rtp_kill ( RTPSession *session, Messenger *messenger ) void rtp_kill ( RTPSession *session, Messenger *messenger )

View File

@ -31,10 +31,12 @@
#define MAX_SEQU_NUM 65535 #define MAX_SEQU_NUM 65535
#define MAX_RTP_SIZE 65535 #define MAX_RTP_SIZE 65535
typedef enum {
rtp_ErrorSending = -40
} RTPError;
/** /**
* Standard rtp header * Standard rtp header
*/ */
typedef struct _RTPHeader { typedef struct _RTPHeader {
uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
@ -46,7 +48,6 @@ typedef struct _RTPHeader {
} RTPHeader; } RTPHeader;
/** /**
* Standard rtp extension header. * Standard rtp extension header.
*/ */
@ -57,7 +58,6 @@ typedef struct _RTPExtHeader {
} RTPExtHeader; } RTPExtHeader;
/** /**
* Standard rtp message. * Standard rtp message.
*/ */
@ -71,14 +71,8 @@ typedef struct _RTPMessage {
struct _RTPMessage *next; struct _RTPMessage *next;
} RTPMessage; } RTPMessage;
/** /**
* Our main session descriptor. * RTP control session.
* It measures the session variables and controls
* the entire session. There are functions for manipulating
* the session so tend to use those instead of directly modifying
* session parameters.
*
*/ */
typedef struct _RTPSession { typedef struct _RTPSession {
uint8_t version; uint8_t version;

View File

@ -42,9 +42,8 @@ typedef struct Messenger Tox;
/* Assume 24 fps*/ /* Assume 24 fps*/
#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
/* call index invalid: true if invalid */ /* true if invalid call index */
#define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls) #define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max)
const ToxAvCSettings av_DefaultSettings = { const ToxAvCSettings av_DefaultSettings = {
av_TypeAudio, av_TypeAudio,
@ -62,32 +61,28 @@ const ToxAvCSettings av_DefaultSettings = {
static const uint32_t jbuf_capacity = 6; static const uint32_t jbuf_capacity = 6;
static const uint8_t audio_index = 0, video_index = 1; static const uint8_t audio_index = 0, video_index = 1;
typedef struct _CallSpecific { typedef struct _ToxAvCall {
pthread_mutex_t mutex[1];
RTPSession *crtps[2]; /** Audio is first and video is second */ RTPSession *crtps[2]; /** Audio is first and video is second */
CSSession *cs;/** Each call have its own encoders and decoders. CSSession *cs;
* You can, but don't have to, reuse encoders for _Bool active;
* multiple calls. If you choose to reuse encoders, } ToxAvCall;
* make sure to also reuse encoded payload for every call.
* Decoders have to be unique for each call.
*/
_Bool call_active;
pthread_mutex_t mutex;
} CallSpecific;
struct _ToxAv { struct _ToxAv {
Messenger *messenger; Messenger *messenger;
MSISession *msi_session; /** Main msi session */ MSISession *msi_session; /** Main msi session */
CallSpecific *calls; /** Per-call params */ ToxAvCall *calls; /** Per-call params */
uint32_t max_calls; uint32_t max_calls;
PAIR(ToxAvAudioCallback, void*) acb;
PAIR(ToxAvVideoCallback, void*) vcb;
/* Decode time measure */ /* Decode time measure */
int32_t dectmsscount; /** Measure count */ int32_t dectmsscount; /** Measure count */
int32_t dectmsstotal; /** Last cycle total */ int32_t dectmsstotal; /** Last cycle total */
int32_t avgdectms; /** Average decoding time in ms */ int32_t avgdectms; /** Average decoding time in ms */
}; };
static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from)
{ {
assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
@ -101,7 +96,6 @@ static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
} }
ToxAv *toxav_new( Tox *messenger, int32_t max_calls) ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
{ {
ToxAv *av = calloc ( sizeof(ToxAv), 1); ToxAv *av = calloc ( sizeof(ToxAv), 1);
@ -114,14 +108,14 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
av->messenger = (Messenger *)messenger; av->messenger = (Messenger *)messenger;
av->msi_session = msi_new(av->messenger, max_calls); av->msi_session = msi_new(av->messenger, max_calls);
av->msi_session->agent_handler = av; av->msi_session->agent_handler = av;
av->calls = calloc(sizeof(CallSpecific), max_calls); av->calls = calloc(sizeof(ToxAvCall), max_calls);
av->max_calls = max_calls; av->max_calls = max_calls;
unsigned int i; unsigned int i;
for (i = 0; i < max_calls; ++i) { for (i = 0; i < max_calls; ++i) {
if (pthread_mutex_init(&av->calls[i].mutex, NULL) != 0 ) { if (create_recursive_mutex(av->calls[i].mutex) != 0 ) {
LOGGER_WARNING("Failed to init call mutex!"); LOGGER_WARNING("Failed to init call(%u) mutex!", i);
msi_kill(av->msi_session); msi_kill(av->msi_session);
free(av->calls); free(av->calls);
@ -145,9 +139,10 @@ void toxav_kill ( ToxAv *av )
if ( av->calls[i].crtps[video_index] ) if ( av->calls[i].crtps[video_index] )
rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
if ( av->calls[i].cs ) cs_kill(av->calls[i].cs); if ( av->calls[i].cs )
cs_kill(av->calls[i].cs);
pthread_mutex_destroy(&av->calls[i].mutex); pthread_mutex_destroy(av->calls[i].mutex);
} }
msi_kill(av->msi_session); msi_kill(av->msi_session);
@ -161,16 +156,12 @@ uint32_t toxav_do_interval(ToxAv *av)
int i = 0; int i = 0;
uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */
for (; i < av->max_calls; i ++) if (av->calls[i].call_active) { for (; i < av->max_calls; i ++) if (av->calls[i].active) {
/* This should work. Video payload will always come in greater intervals */ /* This should work. Video payload will always come in greater intervals */
rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
} }
if (rc < av->avgdectms) { return rc < av->avgdectms ? 0 : rc - av->avgdectms;
return 0;
} else {
return rc - av->avgdectms;
}
} }
void toxav_do(ToxAv *av) void toxav_do(ToxAv *av)
@ -182,11 +173,12 @@ void toxav_do(ToxAv *av)
uint32_t i = 0; uint32_t i = 0;
for (; i < av->max_calls; i ++) { for (; i < av->max_calls; i ++) {
pthread_mutex_lock(&av->calls[i].mutex); pthread_mutex_lock(av->calls[i].mutex);
if (av->calls[i].call_active) cs_do(av->calls[i].cs); if (av->calls[i].active)
cs_do(av->calls[i].cs);
pthread_mutex_unlock(&av->calls[i].mutex); pthread_mutex_unlock(av->calls[i].mutex);
} }
uint64_t end = current_time_monotonic(); uint64_t end = current_time_monotonic();
@ -206,14 +198,16 @@ void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallb
msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata);
} }
void toxav_register_audio_callback(ToxAvAudioCallback cb, void *userdata) void toxav_register_audio_callback(ToxAv* av, ToxAvAudioCallback cb, void* userdata)
{ {
cs_register_audio_callback(cb, userdata); av->acb.first = cb;
av->acb.second = userdata;
} }
void toxav_register_video_callback(ToxAvVideoCallback cb, void *userdata) void toxav_register_video_callback(ToxAv* av, ToxAvVideoCallback cb, void* userdata)
{ {
cs_register_video_callback(cb, userdata); av->vcb.first = cb;
av->vcb.second = userdata;
} }
int toxav_call (ToxAv *av, int toxav_call (ToxAv *av,
@ -227,86 +221,46 @@ int toxav_call (ToxAv *av,
int toxav_hangup ( ToxAv *av, int32_t call_index ) int toxav_hangup ( ToxAv *av, int32_t call_index )
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
if ( av->msi_session->calls[call_index]->state != call_active ) {
return av_ErrorInvalidState;
}
return msi_hangup(av->msi_session, call_index); return msi_hangup(av->msi_session, call_index);
} }
int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings )
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
if ( av->msi_session->calls[call_index]->state != call_starting ) {
return av_ErrorInvalidState;
}
return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
} }
int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason )
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
if ( av->msi_session->calls[call_index]->state != call_starting ) {
return av_ErrorInvalidState;
}
return msi_reject(av->msi_session, call_index, reason); return msi_reject(av->msi_session, call_index, reason);
} }
int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason )
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
if ( av->msi_session->calls[call_index]->state != call_inviting ) {
return av_ErrorInvalidState;
}
return msi_cancel(av->msi_session, call_index, peer_id, reason); return msi_cancel(av->msi_session, call_index, peer_id, reason);
} }
int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings)
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings));
} }
int toxav_stop_call ( ToxAv *av, int32_t call_index ) int toxav_stop_call ( ToxAv *av, int32_t call_index )
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
return av_ErrorNoCall;
}
return msi_stopcall(av->msi_session, call_index); return msi_stopcall(av->msi_session, call_index);
} }
int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video )
{ {
if ( !av->msi_session || cii(call_index, av->msi_session) || if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) ||
!av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer || !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer ||
av->calls[call_index].call_active) { av->calls[call_index].active) {
LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
return av_ErrorInternal; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
const ToxAvCSettings *c_peer = toxavcsettings_cast const ToxAvCSettings *c_peer = toxavcsettings_cast
(&av->msi_session->calls[call_index]->csettings_peer[0]); (&av->msi_session->calls[call_index]->csettings_peer[0]);
const ToxAvCSettings *c_self = toxavcsettings_cast const ToxAvCSettings *c_self = toxavcsettings_cast
@ -332,27 +286,33 @@ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_vide
if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) {
LOGGER_ERROR("Error while starting Codec State!\n"); LOGGER_ERROR("Error while starting Codec State!\n");
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorInternal; return av_ErrorInitializingCodecs;
} }
call->cs->agent = av; call->cs->agent = av;
call->cs->call_idx = call_index; call->cs->call_idx = call_index;
call->cs->acb.first = av->acb.first;
call->cs->acb.second = av->acb.second;
call->cs->vcb.first = av->vcb.first;
call->cs->vcb.second = av->vcb.second;
call->crtps[audio_index] = call->crtps[audio_index] =
rtp_new(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]);
if ( !call->crtps[audio_index] ) { if ( !call->crtps[audio_index] ) {
LOGGER_ERROR("Error while starting audio RTP session!\n"); LOGGER_ERROR("Error while starting audio RTP session!\n");
pthread_mutex_unlock(&call->mutex); goto error;
return av_ErrorInternal;
} }
call->crtps[audio_index]->cs = call->cs; call->crtps[audio_index]->cs = call->cs;
if ( support_video ) { if ( support_video ) {
call->crtps[video_index] = call->crtps[video_index] =
rtp_new(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]);
if ( !call->crtps[video_index] ) { if ( !call->crtps[video_index] ) {
LOGGER_ERROR("Error while starting video RTP session!\n"); LOGGER_ERROR("Error while starting video RTP session!\n");
@ -362,35 +322,37 @@ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_vide
call->crtps[video_index]->cs = call->cs; call->crtps[video_index]->cs = call->cs;
} }
call->call_active = 1; call->active = 1;
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorNone; return av_ErrorNone;
error: error:
rtp_kill(call->crtps[audio_index], av->messenger); rtp_kill(call->crtps[audio_index], av->messenger);
rtp_kill(call->crtps[video_index], av->messenger); rtp_kill(call->crtps[video_index], av->messenger);
cs_kill(call->cs); cs_kill(call->cs);
memset(call, 0, sizeof(CallSpecific)); memset(call, 0, sizeof(ToxAvCall));
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorInternal; return av_ErrorCreatingRtpSessions;
} }
int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) int toxav_kill_transmission ( ToxAv *av, int32_t call_index )
{ {
if (cii(call_index, av->msi_session)) { if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
LOGGER_WARNING("Invalid call index: %d", call_index); LOGGER_WARNING("Invalid call index: %d", call_index);
return av_ErrorNoCall; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
if (!call->call_active) { if (!call->active) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorInvalidState;
} }
call->active = 0;
rtp_kill(call->crtps[audio_index], av->messenger); rtp_kill(call->crtps[audio_index], av->messenger);
call->crtps[audio_index] = NULL; call->crtps[audio_index] = NULL;
@ -398,20 +360,23 @@ int toxav_kill_transmission ( ToxAv *av, int32_t call_index )
call->crtps[video_index] = NULL; call->crtps[video_index] = NULL;
cs_kill(call->cs); cs_kill(call->cs);
call->cs = NULL; call->cs = NULL;
call->call_active = 0; pthread_mutex_unlock(call->mutex);
pthread_mutex_unlock(&call->mutex);
return av_ErrorNone; return av_ErrorNone;
} }
static int toxav_send_rtp_payload(ToxAv *av, static int toxav_send_rtp_payload(ToxAv *av,
CallSpecific *call, ToxAvCall *call,
ToxAvCallType type, ToxAvCallType type,
const uint8_t *payload, const uint8_t *payload,
unsigned int length) unsigned int length)
{ {
if (length > MAX_CRYPTO_DATA_SIZE) {
LOGGER_WARNING("Size exceeds limit: %d", length);
return av_ErrorUnknown;
}
if (call->crtps[type - av_TypeAudio]) { if (call->crtps[type - av_TypeAudio]) {
/* Audio */ /* Audio */
@ -420,8 +385,7 @@ static int toxav_send_rtp_payload(ToxAv *av,
/* Video */ /* Video */
int parts = cs_split_video_payload(call->cs, payload, length); int parts = cs_split_video_payload(call->cs, payload, length);
if (parts < 0) return parts;
if (parts == -1) return av_ErrorInternal;
uint16_t part_size; uint16_t part_size;
const uint8_t *iter; const uint8_t *iter;
@ -431,8 +395,8 @@ static int toxav_send_rtp_payload(ToxAv *av,
for (i = 0; i < parts; i++) { for (i = 0; i < parts; i++) {
iter = cs_get_split_video_frame(call->cs, &part_size); iter = cs_get_split_video_frame(call->cs, &part_size);
if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) != 0) if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0)
return av_ErrorInternal; return av_ErrorSendingPayload;
} }
return av_ErrorNone; return av_ErrorNone;
@ -442,32 +406,32 @@ static int toxav_send_rtp_payload(ToxAv *av,
int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input)
{ {
if (cii(call_index, av->msi_session)) { if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
LOGGER_WARNING("Invalid call index: %d", call_index); LOGGER_WARNING("Invalid call index: %d", call_index);
return av_ErrorNoCall; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
if (!call->call_active) { if (!call->active) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorInvalidState;
} }
if (cs_set_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { if (cs_set_video_encoder_resolution(call->cs, input->d_w, input->d_h) < 0) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorInternal; return av_ErrorSettingVideoResolution;
} }
int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
if ( rc != VPX_CODEC_OK) { if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc));
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorInternal; return av_ErrorEncodingVideo;
} }
++call->cs->frame_counter; ++call->cs->frame_counter;
@ -479,7 +443,7 @@ int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, in
while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) {
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
if ( copied + pkt->data.frame.sz > dest_max ) { if ( copied + pkt->data.frame.sz > dest_max ) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return av_ErrorPacketTooLarge; return av_ErrorPacketTooLarge;
} }
@ -488,30 +452,30 @@ int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, in
} }
} }
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return copied; return copied;
} }
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size)
{ {
if (cii(call_index, av->msi_session)) { if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
LOGGER_WARNING("Invalid call index: %d", call_index); LOGGER_WARNING("Invalid call index: %d", call_index);
return av_ErrorNoCall; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
if (!call->call_active) { if (!call->active) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorInvalidState;
} }
int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size);
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return rc; return rc;
} }
@ -523,27 +487,27 @@ int toxav_prepare_audio_frame ( ToxAv *av,
const int16_t *frame, const int16_t *frame,
int frame_size) int frame_size)
{ {
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->calls[call_index].active) {
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
if (!call->call_active) { if (!call->active) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorInvalidState;
} }
int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max);
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
if (rc < 0) { if (rc < 0) {
LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc));
return av_ErrorInternal; return av_ErrorEncodingAudio;
} }
return rc; return rc;
@ -551,35 +515,32 @@ int toxav_prepare_audio_frame ( ToxAv *av,
int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
{ {
if (size > MAX_CRYPTO_DATA_SIZE) if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->calls[call_index].active) {
return av_ErrorInternal;
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorNoCall;
} }
CallSpecific *call = &av->calls[call_index]; ToxAvCall *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex); pthread_mutex_lock(call->mutex);
if (!call->call_active) { if (!call->active) {
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index); LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall; return av_ErrorInvalidState;
} }
int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size);
pthread_mutex_unlock(&call->mutex); pthread_mutex_unlock(call->mutex);
return rc; return rc;
} }
int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest )
{ {
if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) ||
|| av->msi_session->calls[call_index]->peer_count <= peer ) !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer )
return av_ErrorInternal; return av_ErrorNoCall;
*dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]);
return av_ErrorNone; return av_ErrorNone;
@ -587,16 +548,16 @@ int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSe
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer )
{ {
if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index]
|| av->msi_session->calls[call_index]->peer_count <= peer ) || av->msi_session->calls[call_index]->peer_count <= peer )
return av_ErrorInternal; return av_ErrorNoCall;
return av->msi_session->calls[call_index]->peers[peer]; return av->msi_session->calls[call_index]->peers[peer];
} }
ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
{ {
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] )
return av_CallNonExistent; return av_CallNonExistent;
return av->msi_session->calls[call_index]->state; return av->msi_session->calls[call_index]->state;
@ -605,7 +566,7 @@ ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability )
{ {
return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CsCapabilities) capability : 0; return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0;
/* 0 is error here */ /* 0 is error here */
} }
@ -614,35 +575,17 @@ Tox *toxav_get_tox(ToxAv *av)
return (Tox *)av->messenger; return (Tox *)av->messenger;
} }
int toxav_set_vad_treshold(ToxAv *av, int32_t call_index, uint32_t treshold)
{
if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState;
/* TODO on't use default framedur... */
cs_set_vad_treshold(av->calls[call_index].cs, treshold, av_DefaultSettings.audio_frame_duration);
return av_ErrorNone;
}
int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref)
{
if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState;
return cs_calculate_vad(av->calls[call_index].cs, PCM, frame_size, ref);
}
int toxav_get_active_count(ToxAv *av) int toxav_get_active_count(ToxAv *av)
{ {
if (!av) return av_ErrorInternal; if (!av) return -1;
int rc = 0, i = 0; int rc = 0, i = 0;
for (; i < av->max_calls; i ++) if (av->calls[i].call_active) rc++; for (; i < av->max_calls; i ++) if (av->calls[i].active) rc++;
return rc; return rc;
} }
/* Create a new toxav group. /* Create a new toxav group.
* *
* return group number on success. * return group number on success.

View File

@ -81,17 +81,26 @@ typedef enum {
} ToxAvCallState; } ToxAvCallState;
/** /**
* Error indicators. * Error indicators. Values under -20 are reserved for toxcore.
*/ */
typedef enum { typedef enum {
av_ErrorNone = 0, av_ErrorNone = 0,
av_ErrorInternal = -1, /* Internal error */ av_ErrorUnknown = -1, /* Unknown error */
av_ErrorAlreadyInCall = -2, /* Already has an active call */ av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
av_ErrorNoCall = -3, /* Trying to perform call action while not in a call */ av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
av_ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
av_ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
av_ErrorInvalidCodecState = -6, /* Codec state not initialized */ av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */
av_ErrorPacketTooLarge = -7, /* Split packet exceeds it's limit */ 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; } ToxAvError;
@ -153,12 +162,12 @@ void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallba
/** /**
* Register callback for audio data. * Register callback for audio data.
*/ */
void toxav_register_audio_callback (ToxAvAudioCallback cb, void *userdata); void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata);
/** /**
* Register callback for video data. * Register callback for video data.
*/ */
void toxav_register_video_callback (ToxAvVideoCallback cb, void *userdata); void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata);
/** /**
* Call user. Use its friend_id. * Call user. Use its friend_id.
@ -266,18 +275,6 @@ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilitie
*/ */
Tox *toxav_get_tox (ToxAv *av); Tox *toxav_get_tox (ToxAv *av);
/**
* Set VAD activity treshold for calculating VAD. 40 is some middle value for treshold
*/
int toxav_set_vad_treshold (ToxAv *av, int32_t call_index, uint32_t treshold);
/**
* Check if there is activity in the PCM data.
* Activity is present if the calculated PCM energy is > ref_energy.
* Returns bool.
*/
int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref);
/** /**
* Returns number of active calls or -1 on error. * Returns number of active calls or -1 on error.
*/ */

View File

@ -2696,22 +2696,12 @@ Net_Crypto *new_net_crypto(DHT *dht, TCP_Proxy_Info *proxy_info)
if (temp == NULL) if (temp == NULL)
return NULL; return NULL;
pthread_mutexattr_t attr; if (create_recursive_mutex(&temp->tcp_mutex) != 0 ||
pthread_mutex_init(&temp->connections_mutex, NULL) != 0) {
if (pthread_mutexattr_init(&attr) == 0) {
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0 || pthread_mutex_init(&temp->tcp_mutex, &attr) != 0
|| pthread_mutex_init(&temp->connections_mutex, NULL) != 0) {
pthread_mutexattr_destroy(&attr);
free(temp); free(temp);
return NULL; return NULL;
}
} else {
free(temp);
return NULL;
} }
pthread_mutexattr_destroy(&attr);
temp->dht = dht; temp->dht = dht;
new_keys(temp); new_keys(temp);

View File

@ -162,3 +162,22 @@ int load_state(load_state_callback_func load_state_callback, void *outer,
return length == 0 ? 0 : -1; return length == 0 ? 0 : -1;
}; };
int create_recursive_mutex(pthread_mutex_t* mutex)
{
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0)
return -1;
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
return -1;
/* Create queue mutex */
if (pthread_mutex_init(mutex, &attr) != 0)
return -1;
pthread_mutexattr_destroy(&attr);
return 0;
}

View File

@ -27,6 +27,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <pthread.h>
#define MIN(a,b) (((a)<(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b))
@ -52,4 +53,6 @@ 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, int load_state(load_state_callback_func load_state_callback, void *outer,
const uint8_t *data, uint32_t length, uint16_t cookie_inner); const uint8_t *data, uint32_t length, uint16_t cookie_inner);
int create_recursive_mutex(pthread_mutex_t* mutex);
#endif /* __UTIL_H__ */ #endif /* __UTIL_H__ */